Wednesday, February 15, 2017

Why might Core Data fail to merge data from private context to main context?

Leave a Comment

I have an application using Core Data with the following, fairly standard, managed object context hierarchy:

Persistent Store Coordinator      ↳ Save Context (Private Queue Concurrency Type)          ↳ Main Context (Main Queue Concurrency Type)         ↳ Private Context (Private Queue Concurrency Type) 

The merge policy for all managed object contexts is set to NSMergeByPropertyObjectTrumpMergePolicy

I am observing NSManagedObjectContextDidSaveNotification which will invoke the following function when the Private Context is saved and merge changes to the Main Context:

func contextDidSaveNotificationHandler(notification: NSNotification) {      if let savedContext = notification.object as? NSManagedObjectContext {         if savedContext == privateObjectContext {              mainObjectContext.performBlock({                  if let updatedObjects = notification.userInfo![NSUpdatedObjectsKey] as? Set<NSManagedObject> {                     //                     // fire faults on the updated objects                     //                     for obj in updatedObjects {                         mainObjectContext.objectWithID(obj.objectID).willAccessValueForKey(nil)                     }                 }                  mainObjectContext.mergeChangesFromContextDidSaveNotification(notification)             })         }     } } 

This is working most of the time but sometimes I am finding that changes to existing objects in the Private Context are not being merged into the Main Context. I can't figure out why -- the private context save is successful; the NSManagedObjectContextDidSaveNotification notification is being sent; the notification handler is being invoked; notification.userInfo?[NSUpdatedObjectsKey] contains the correctly updated objects; but at the end, the main context is not synchronized with the private context. (ie: the managed objects in the main context are not in sync with the values contained in notification.userInfo?[NSUpdatedObjectsKey]) If I kill the app and relaunch it, the contexts become synchronized again (after loading objects from the persistent store).

I have -com.apple.CoreData.ConcurrencyDebug 1 enabled in my launch arguments, and all Core Data multithreading rules are being followed. I can't see anything overtly wrong with my managed object context hierarchy or the merging function. What could possibly be causing this?

3 Answers

Answers 1

You can't merge sibling context. You have to merge from the parent down. In other words change

 if savedContext == privateObjectContext { 

to

 if savedContext == savingObjectContext { 

where the savingObjectContext is the parent context of the main context.

Answers 2

You could always set your mainQueueContext as your privateQueueContext's parent:

privateObjectContext.parent = mainObjectContext 

This will merge your saves into your main object context. I used this in a project where I parse JSON, and set core data objects in a privateQueue's perform block, with my mainContext set as the parent. Then I simple save the private, and then access the data from the main thread/main context. Works like a charm. I should add I do not keep the private context in memory, it is created new when I need it.

Answers 3

The problem is with your merge policy. Try changing to to NSErrorMergePolicy and I think you will start to see merge conflicts. At the least this will give you more of an idea of the underlying cause

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment