How do I create / use temporary NSManagedObjects?


Simple, common pattern I can't find in Apple's docs:

  1. Load a coredata store
  2. Download new data, creating objects in memory
  3. Save some of the new data to the store (usually "only the new bits / bits that haven't changed")

Instead, I can find these alternatives, none of which are correct:

  1. Don't create objects in memory (well, this means throwing away everything good about objects. Writing your code using lots of NSDictionary's who serve no purpose except to workaround CoreData's failings. Not workable in general)
  2. Create objects, but then delete the ones you don't want (Apple suggest this in docs, but their Notifications go horribly wrong: those "deletes" show up when you try to save, even though they shouldn't / can't)
  3. Create objects in a secondary Context (Apple strongly implies this is correct, but apparently doesn't provide any way for you to move objects from the temp context to the real one, without doing the above (deleting objects you just created, then doing a save). That's not possible in general, because the objects often need to be hooked-up to references in the new context, and a save will fail)

Surely, it shouldn't be this difficult?

If I have to write all the code to manually deep copy an object (by iterating down all of its fields and data structures), why does CoreData exist in the first place? This is a basic feature that CD provides internally.

The only solution I've had working so far is option 2 (from apple's docs), with custom heuristics to "guess" when Apple is sending NSNotifications for objects that should never have been saved in the first place (but Apple sends notofications for anyway). That's a horrible hack.

EDIT: clarification:

I can't figure out how to get Apple's Notifications to be delivered correctly. Apple's code seems to convert insertions into "updates", and convert "temporary objects" into "deletes", etc. I can't listen for "new objects".

There are 3 common ways to handle this.

  1. Create a temporary object context with the same persistent store as your "real" context, add your objects to this temporary context, and once you know which objects you want to keep, remove all the others from your temporary context and save the temporary context. When you save, you can update your "real" context by observing the NSManagedObjectContextDidSaveNotification notification and merge it into your "real" context (ala [realContext mergeChangesFromContextDidSaveNotification:notification]). See Mike Weller's answer here for details.

    (If you're concerned about I/O, you could use an in-memory context, which has pros and cons.)

  2. Instead of using an NSManagedObject, use an NSDictionary. Once you know which objects you want to keep, instantiate a new managed object and call [managedObject setValuesForKeysWithDictionary:temporaryObject] to copy the values from the temporary object into the managed object, then save your "real" context. If you have code that needs to work with NSManagedObjects and temporary objects (e.g., a table view), you'd write that code using key-value coding (aka valueForKey:, setValue:forKeyPath:).

  3. Add an "isTemporary" attribute to your entity model (defaulting to NO). When you create a temporary object, set isTemporary to YES and insert the object into your "real" context. Once you know which objects you want to keep, change their isTemporary attribute to NO. Of course, you need to periodically delete these temporary objects, but that's easy to do (e.g., when that task is completed, on app exit, etc).

The advantages of #1 and #3 are that your objects live in the CoreData world -- e.g., they can be queried, they can participate in relationships, etc. The advantage of #2 is that it's light and fast, especially if you have lots of temporary objects.