Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Newly created NSManagedObject returns temporary objectID even after save

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;
    }
}
like image 951
Shinigami Avatar asked Aug 23 '13 19:08

Shinigami


2 Answers

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.

Update 1

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.

like image 162
Marcus S. Zarra Avatar answered Nov 15 '22 06:11

Marcus S. Zarra


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.

like image 12
Nathan Jones Avatar answered Nov 15 '22 06:11

Nathan Jones