Working environment: OS X 10.6.3, Xcode 3.2.1
Hi! I'm working on a project called Rent-a-Flick. The project has two entities: Movie and Client. Between them there's a many-to-many relationship.
I have 2 tables: one with the movies and one with the clients. Their content is bound to the proper array controllers(for movie and client).
I want to add a third table in which only the clients that rented a selected movie will appear. I should also be able to add/remove clients from this table. How can I do that without creating duplicates?
The project is open source. I'll publish it as soon as I make a stable release.
This "third table" already exists implicitly in your Core Data many-to-many relationship.
When you call a method like [aMovie addClientObject:aClient] on one of your Core Data model objects, the effect of that is to add a row to the movie-client relationship table. Similarly, if you want to remove a client, you would do it using the Core Data accessor method [aMovie removeClientObject:aClient], not by directly manipulating the table.
If you have an instance of a Movie object, you can see what clients rented that movie simply by referring to the clients property of that object. For example:
NSArray *rented_clients = [[aMovie.clients] allObjects];
Related
Prerequisites:
I have 2 methods in the network API:
return list of entities (just basic info: name, id, etc.)
return detailed info about entity
The requirement is to save only objects created by processing the second request (save to sqlite) and leave non-full objects without saving.
Also, the 'main' context should contain only full objects from 1st request, and any other 'temporary' context should contain all the others objects.
I've tried to create two instances of NSPersistentStoreCoordinator and use them for different types of contexts, but it seems that for one NSManagedObjectModel can exist only one coordinator (the pointer points to the same adress).
If I understand you correctly, then I think your best option is to only create a managed object once you're sure you want it to persist in Core Data storage. You may need another layer of non-managed objects to contain data for the "non-full" entities
(This would be something like Data Transfer Objects from Java EE programming.)
You can not save indiscriminately from within the same MOC. Saving the MOC always saves everything in it.
So, you can use a separate MOC that is never saved, and then just "move" those objects to the main MOC when they are ready to be saved.
The solution that exactly solves my problem is to create two 'forks' of core data stack:
one with default config and mainContext
the second (new instance of the same NSManagedObjectModel, new
NSPersistentStore (inMemory) and new NSPersistenStoreCoordinator )
I need to populate my map with annotations. Each annotation has corresponding Place resource that is being fetched from remote server. Each Place has associated Category - it is fetched from the server too as a separate resource.
Let's assume that to populate a given region I need to fetch 100 places, each belonging to the one of 20 categories (actually there are much more of them).
I use AFNetworking to fetch the both of resources. I try to cache both places and categories for offline use, so before the annotations are displayed on map, I write fetched resources to the Core Data tables.
Each place retrieves its associated category resource by demand and I need to write both a place in the 'places' table, and category in the 'categories' table.
Because of fetching is being done asynchronously, when writing particular category to table I can't know if maybe another place "thread" attempts to write the same associated category to 'categories' table.
So, the question is: what is the pattern for working with Core Data tables, when they need to be populated with information retrieved asynchronously? Specifically how any given thread which is going to write a category could know that there is already one trying to do that?
UPDATE 1: My current problem is that currently I am having the duplication of categories. My guess is that obviously each category which attempts to be written is not aware about the parallel write of the same category.
UPDATE 2: The most simple description of my case is the following:
I create a new Category entity with some fields in one thread and in the meantime in another thread I create exactly the same Category entity with the same fields aiming to be the same Category object like the first thread has.
One thread wins calling [managedObjectContext save:&error], but then before the actual record is appeared in the PersistentStore, the second does call the save too. The question is: how to prevent the duplication of records in 'categories' table?
UPDATE 3: I am considering both variants of using managed object contexts: 1) reusing one shared moc instance by all threads 2) instantiate a new moc on each thread
Thanks!
The "official" answer is going to be something about using an NSOperationQueue and/or taking manual steps to ensure that all your accesses to the NSManagedObjectContext occur on the same thread that created the context. There are a number of references and tutorials that you can follow to implement this approach.
As an alternative, there's a thread-safe Core Data extension on github that will do this for you. If you use it, it will automatically synchronize your database operations so that you don't have to worry about whether or not another thread is doing something with the context. You can just insert things as they come in, and the framework will ensure that your operations are translated into something that won't make Core Data explode.
Full disclosure: I built the github project.
For example. if I have a chat app that uses objects like: ChatRoom and ChatMessage. (both subclasses of NSManagedObject)
And throughout the app I need to: search chat rooms / add massages / create chat rooms and messages / and any other manipulate.
Is it ok if I do everything directly with core data? I mean every time I need to search a chat room or something like that, to do it with NSFetchRequest or NSFetchedResultsController.
You can access the data whenever you need, of course. On the other hand you should try to use caching mechanism as much as possible.
For example, if you are using your data in UITableView you should defintely go with NSFetchedResultsController as it was created explicitly for UITableViews. From the apple docs on NSFetchedResultsController:
It optionally monitors changes to objects in its associated managed object context, and reports changes in the results set to its delegate (see “The Controller’s Delegate”).
It optionally caches the results of its computation so that if the same data is subsequently re-displayed, the work does not have to be repeated (see “The Cache”).
Otherwise, if you need the data just temporarily, you can access them of course using NSFetchRequest each time they are needed or cache them in your business objects if they don't change or you know their lifetime otherwise.
I'm writing an API method that returns a list of objects from a web service. This service also caches results and attempts to return the cached results (if any) before hitting the web service again. So I'm storing this list in a Core Data entity. But Core Data only allows to-many relationships to be stored in an NSSet, which does not preserve order. But I want the cached results to retain the original order (which was originally from the web service). I don't necessarily know how that order is established (so I can't sort).
So how can I preserve this order? My plan is to store a string with the object ids which I can later use to order them:
NSString *objectIds = #"1 5 2 9 4";
Is this the best way to do it?
If you can target iOS 5 then you can use the new NSOrderedSet feature for arbitrarily-ordered Core Data relationships. See the Core Data Release Notes for iOS 5 for more information about this new feature.
If you have to support iOS 4 then you will need to handle the ordering yourself. A common way to do this is to add an integer property to your model which you then need to manage yourself. You can then sort on this property when fetching your data.
Storing the order in a completely different string seems like the wrong way to do it. If the ordering is important this information should be in the model and the user should be able to query for it directly ("give me the first five objects of this particular list", or "what is the index of object foo"). Having to get a string of object IDs, parse it, and then query for certain object IDs feels very wrong to me.
Include an NSNumber id field of your own in the CoreData model in which you are storing the objects. The id will represent the ordering you want to preserve; you'll have to set it yourself, obviously. Then when you fetch the objects from CoreData you can do so sorted by that id. You may want/have to maintain somewhere the highest id value you've assigned to retain ordering correctly with subsequent web service calls and app launches.
That's a subjective question. You can also set them up as a linked list in the order you want them in.
I've got a store that is synchronized externally and a store that is unique to the application instance, so in order to cleanly differentiate the two I want to have some join entities between them and then resolve through to the entities between using Fetched Properties, as "discussed" in the Core Data Programming Guide:
developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html#//apple_ref/doc/uid/TP40001857-SW5
I think I just don't really "get" how Fetched Properties are supposed to be used - and I've spent a fair number of hours looking for examples with no real luck.
The way I think of it is,
I have the following Entities each in a different store
Foo with attribute relatedBarName in store A
Bar with attribute barName in store B
I need to create a fetched property on Foo named findRelatedBar that relates Foo to Bar loosely through barName = relatedBarName.
However, I don't understand how since Foo and Bar are in different stores how to declare any relationship of any sort, whether through the fetched property or not, from Foo to Bar?
The predicate builder in XCode seems to want a Destination entity. If they are in different schemas, how can you declare the destination? If you don't declare a destination, how do you at runtime indicate that findRelatedBar on Foo is describing Bar?
Otherwise, do they need to be in the same schema but just stored in different stores?
In crafting this question, I thought of these questions and answered them myself by more focused examination of the documentation. I assume if I found it confusing, others might as well, so I'll inline them with this post to make it easier to find related answers to fetched properties / core data stores.
Q) If a store coordinator have more than one store associated with it of the same schema, how do insertions know which store to insert to?
A) You use the assignObject:toPersistentStore: method on the managed object context.
Q) What does FETCH_SOURCE refer to in specific?
A) It's simply the managed object which has the fetched property associated with it. Sort of like "self"
Q) What does FETCHED_PROPERTY refer to in specific?
A) It is a reference to the fetched property description instance you are using to query with - you can use this to insert per query variable substitution. By setting a property (as in the Core Data Programming example) on the userInfo of the property description instance you're using, you can inject that value into the expression.
Thanks!!!!
The answer is:
Yes, you need to do a cross store fetched property with shared schemas. If you do this, you need to make sure you attribute the inserts with the assignObject:: method as described in the question. However, due to the limitations of the SQLLITE persistent store, natural things like IN $FETCH_SOURCE.attribute do not work.
Q) If a store coordinator have more
than one store associated with it of
the same schema, how do insertions
know which store to insert to?
This is what configurations are for. You create a configuration for each store and then assign entities to that configuration. You then create the store with the proper configuration. When you save the context, each entity will automatically go to the correct store.