First, when sending a master data code, I suggest that you do not send a zip code depending on a third-party library unless this third-party library is directly related to your problem. I assume that MR is a magical record, but I do not use it, and it seems to be only muddy water of the message, because who knows what it (or not) does under the covers.
In other words, try trimming the examples until the code is needed ... and no more ... and if necessary, include third-party libraries.
Secondly, when writing unit tests to use basic data, I suggest using a stack in memory. You always start empty, and it can be initialized, but you want to. Much easier to use for testing.
However, your problem is not understanding what mergeChangesFromContextDidSaveNotification does (and does not).
Basically, you have an object in a persistent repository of master data. You have two different MOCs attached to the repository through the same PSC.
Then your test loads the object in the main MOC and changes the value without saving in PSC. Then the second MOC loads the same object and changes its value to something else (i.e. Storage, and both MOCs have a different value for a specific attribute of the same object).
Now, when we save the MOC, if there are conflicts, conflicts will be processed as directed by mergePolicy . However, the merge policy does not apply to mergeChangesFromContextDidSaveNotification .
You can think of mergeChangesFromContextDidSaveNotification as inserting any new objects, deleting any deleted objects, and “updating” any updated objects while saving any local changes.
In your test, if you add another attribute (for example, “name”) and change the “name” and “location” in the BG MOC, but change only the “location” in the main MOC, you will see that the “name” is combined with the BG MOC in the main MOC, as expected.
However, as you point out in your question, the "location" does not seem to come together. In fact, it merges, but any local changes override what is in the store ... and this is exactly what you want, because the user most likely made this change and does not want it to be changed behind them.
Basically, any pending local changes will override the changes from the MOC to be merged.
If you want something else, you must implement this behavior when you perform a merge, for example ...
- (void)mergeBGChangesToMain:(NSNotification*)note { NSMutableSet *updatedObjectIDs = [NSMutableSet set]; for (NSManagedObject *obj in [note.userInfo objectForKey:NSUpdatedObjectsKey]) { [updatedObjectIDs addObject:[obj objectID]]; } [_mainMOC performBlock:^{ for (NSManagedObject *obj in [_mainMOC updatedObjects]) { if ([updatedObjectIDs containsObject:obj.objectID]) { [_mainMOC refreshObject:obj mergeChanges:NO]; } } [_mainMOC mergeChangesFromContextDidSaveNotification:note]; }]; }
This code first collects the ObjectID each object that has been updated in the unified-of-MOC.
Before performing the merge, we will then review each of the updated objects in the merge-MOC. If we merge an object into our MOC, and our merge-MOC also changes this object, then we want to allow values in unified-from-MOC to override values in unified-MOC. Thus, we update the local object from the repository, basically discarding any local changes (there are side effects, for example, causing the object to become an error, releasing links to any relations and releasing any properties of the transient process - see the documentation for refreshObject: mergeChanges :. )
Consider the following category, which takes into account your situation, and the general problem when using observers such as NSFetchedResultsController .
@interface NSManagedObjectContext (WJHMerging) - (void)mergeChangesIntoContext:(NSManagedObjectContext*)moc withDidSaveNotification:(NSNotification*)notification faultUpdatedObjects:(BOOL)faultUpdatedObjects overrideLocalChanges:(BOOL)overrideLocalChanges completion:(void(^)())completionBlock; @end @implementation NSManagedObjectContext (WJHMerging) - (void)mergeChangesIntoContext:(NSManagedObjectContext *)moc withDidSaveNotification:(NSNotification *)notification faultUpdatedObjects:(BOOL)faultUpdatedObjects overrideLocalChanges:(BOOL)overrideLocalChanges completion:(void (^)())completionBlock { NSAssert(self == notification.object, @"Not called with"); NSSet *updatedObjects = notification.userInfo[NSUpdatedObjectsKey]; NSMutableSet *updatedObjectIDs = nil; if (overrideLocalChanges || faultUpdatedObjects) { updatedObjectIDs = [NSMutableSet setWithCapacity:updatedObjects.count]; for (NSManagedObject *obj in updatedObjects) { [updatedObjectIDs addObject:[obj objectID]]; } } [moc performBlock:^{ if (overrideLocalChanges) { for (NSManagedObject *obj in [moc updatedObjects]) { if ([updatedObjectIDs containsObject:obj.objectID]) { [moc refreshObject:obj mergeChanges:NO]; } } } if (faultUpdatedObjects) { for (NSManagedObjectID *objectID in updatedObjectIDs) { [[moc objectWithID:objectID] willAccessValueForKey:nil]; } } [moc mergeChangesFromContextDidSaveNotification:notification]; if (completionBlock) { completionBlock(); } }]; } @end