Created
March 25, 2013 20:33
-
-
Save lukeredpath/5240453 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I have an entity Foo, which has a 1-n relationship with Bar. Bar has a 1-n relationship with Baz. | |
On the main thread, the UI is showing a list of all the Bars in a single Foo. I am using KVO on Foo's bars relationship to detect when Bars are added to Foo. | |
Using a child MOC of the main MOC (the UI uses the main MOC), I create a new Bar, associate it with a couple of Baz objects, then add it to Foo's bars relationship. | |
Then I save the child MOC, which causes its changes to be merged into the main MOC. | |
At this point, my KVO observer on foo.bars fires, and when I inspect the change dictionary, I see my inserted Bar object however, the Bar's bazes relationship is empty! | |
My KVO observer then fires again immediately, and this time, the change dictionary contains my Bar object and its bazes relationship is not empty. | |
Its not valid for Bar to have an empty set of Baz objects, but as I'm trying to refresh my UI in response to these KVO notifications, at the point the first KVO notification fires, I'm trying to refresh my UI to display an invalid Bar. |
New observation…it appears that the context merge is first inserting a Widget into Inventory, then inserting Frobnicators into the Widget
Test project:
https://dl.dropbox.com/u/570528/TestProject.zip
I think I fixed it. The thing is: you want to save your new Widget before adding the relationship to your Inventory:
if (saved) {
// start observing changes
[inventory addObserver:self forKeyPath:@"widgets" options:NSKeyValueObservingOptionNew context:nil];
NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childContext.parentContext = self.managedObjectContext;
[childContext performBlock:^{
// create new widget with frobnicators on child context
NSManagedObject *widget = [NSEntityDescription insertNewObjectForEntityForName:@"Widget" inManagedObjectContext:childContext];
NSManagedObject *childFrobnicator = [childContext existingObjectWithID:frobnicator.objectID error:nil];
if (childFrobnicator) {
[[widget mutableSetValueForKeyPath:@"frobnicators"] addObject:childFrobnicator];
}
NSLog(@"Inserting widget %@", widget);
__block NSError *error;
// Save the child context here after we created the widget and added the relationship to its
// child frobnicator.
if ([childContext save:&error]) {
NSLog(@"Saved child context before adding widget to inventory");
}
// now add it to the existing inventory object
NSManagedObject *childInventory = [childContext existingObjectWithID:inventory.objectID error:nil];
if (childInventory) {
[[childInventory mutableSetValueForKeyPath:@"widgets"] addObject:widget];
}
// so we can get the widget on the main context below
// If we have this line uncommented, the childContext save below doesn't work. I suppose it is because the
// widget object won't be faulting anymore.
//[childContext obtainPermanentIDsForObjects:@[widget] error:nil];
// do the child/parent save dance
if ([childContext save:&error]) {
NSLog(@"Saved child context");
[self.managedObjectContext performBlock:^{
if ([self.managedObjectContext save:&error]) {
NSLog(@"Saved parent context");
// load the widget on the main context
NSManagedObject *widgetOnMainContext = [self.managedObjectContext existingObjectWithID:widget.objectID error:nil];
if (widgetOnMainContext) {
// this object is the exact same object as the one in our change observer below, but it has frobnicators!
NSLog(@"Widget on main context %@", widgetOnMainContext);
}
};
}];
}
}];
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In short, my expectation is that I should only get one KVO notification, and the inserted Bar should also contain its inserted Baz objects.
If this is normal behaviour, what is a better way to observe changes to Foo.bars such that I'm only notified of changes that are in a consistent state?