Very simple situation. Not sure why it's causing an issue.
I have a view that creates a new NSManagedObject in a child NSManagedObjectContext. When the user presses "done", it saves the child context, then it saves the parent context, then it posts a notification with the newly created object's objectID. In the main view controller, I respond to the notification and try to get the newly created object with existingObjectWithID:error:
.
Problem is this fails because the objectID is temporary (I get a "Cocoa error 133000"). The saves to the two contexts are flawless: when I reload the app, I can see the entries I created. But at the time I need to get a reference to the new object, it fails.
Why does it give me a temporary object ID after I've already saved it?
Note: I have tried using obtainPermanentIDsForObjects:error:
, which works, but the permanent ID still fails when I try to use it to obtain the object.
Here's some code:
-(IBAction)done:(id)sender
{
if ([editorDoneNotification isEqualToString:kNOTIFICATION_OBJECTADDED]) {
// save the temporary moc
NSError* e;
if (![self.tempContext save:&e]) { // this is always a successful save
NSLog(@"Failed to save temporary managed object context: %@", [e localizedDescription]);
[[[UIAlertView alloc] initWithTitle:@"Database Error"
message:@"Failed to add object."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
}
NSError* e;
if (![[[AMDataModel sharedDataModel] mainContext] save:&e]) { // this is also successful
NSLog(@"Failed to save main managed object context: %@", [e localizedDescription]);
[[[UIAlertView alloc] initWithTitle:@"Database Error"
message:@"Failed to edit object."
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
else
[[NSNotificationCenter defaultCenter] postNotificationName:editorDoneNotification object:[self.editingObject objectID]];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
And this is how I respond to the notifications:
-(void)objectAdded:(NSNotification*)notification
{
if (self.popoverController && [self.popoverController isPopoverVisible]) {
[self.popoverController dismissPopoverAnimated:YES];
}
NSManagedObjectID* newObjectID = (NSManagedObjectID*)(notification.object);
NSError* error;
AMObject* object = (AMObject*)[[[AMDataModel sharedDataModel] mainContext] existingObjectWithID:newObjectID error:&error]; // this is where the cocoa error 133000 happens
if (error != nil) {
NSLog(@"ERROR: Could not load new object in main managed object context.");
}
GMSMarker* m = [[GMSMarker alloc] init];
m.position = CLLocationCoordinate2DMake(object.latitudeValue, object.longitudeValue);
m.userData = object;
m.map = self.mapView;
[self.markers addObject:m];
}
-(void)objectEdited:(NSNotification *)notification
{
NSManagedObjectID* editedObjectID = (NSManagedObjectID*)notification.object;
NSError* error = nil;
AMObject* object = (AMObject*)[[[AMDataModel sharedDataModel] mainContext] existingObjectWithID:editedObjectID error:&error];
if (error != nil) {
NSLog(@"Error could not load edited object in main managed object context");
}
//update the UI based on edit
if ([self.popoverController isPopoverVisible]) {
[self.popoverController dismissPopoverAnimated:YES];
self.popoverController = nil;
}
}
Because the child does not get updated back from the parent MOC. The parent MOC will update its own instance of the NSManagedObject
with a permanent ID but that change will not be pushed down to the instance of that NSManagedObject
belonging to the child MOC.
I do not use -objectID
in this situation. It has some uses but it is not a permanent uniqueID. In a situation like this, I prefer to add my own uniqueID to the entity and then fetch it from the main context.
You could also just listen for the context save notifications or use an NSFetchedResultsController which will receive updates.
Have you tried obtaining the permanent ID's for your objects? I've had success sharing NSManagedObjectID
's between contexts by calling
NSError *error = nil;
[self.context obtainPermanentIDsForObjects:@[object1, object2]
error:&error];
followed by my save method. I structure my contexts in such a way that there is an I/O context which is the parent to my 'read context' (context used for my main thread activity) which is the parent to my background task contexts. Save in this case propagates all the way up the chain.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With