Dmitrii V
Dmitrii V

Reputation: 15

Crash when trying to save NSManagedObject

Good day, everyone!

I have a UITableView. I need to add some object (with some properties, in this case I need at least name) to it and when I hit "+" at the navigation bar, new UIViewController presents modally and at viewDidLoad I call this method, that sets default name to it:

- (void)createManagedObject {

    NSManagedObjectContext *context = [[DVCoreDataManager sharedManager] managedObjectContext];
    NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"DVObject" inManagedObjectContext:context];

    [newManagedObject setValue:@"new object" forKey:@"name"];

    NSError *error = nil;
    if (![context save:&error]) {
        NSLog(@"%@", [error localizedDescription]);
    }

    self.currentObjectID = newManagedObject.objectID;
}

After I've entered name in UITextField I hit the "save" button and call that method, that I need to update name of the object:

- (IBAction)actionSaveTraining:(UIBarButtonItem *)sender {

    NSManagedObjectContext *context = [[DVCoreDataManager sharedManager] managedObjectContext];
    NSManagedObject *newManagedObject = [context existingObjectWithID:self.currentObjectID error:nil];

    [newManagedObject setValue:self.nameTextField.text forKey:@"name"];

    NSError *error = nil;
    if (![context save:&error]) {
        NSLog(@"%@", [error localizedDescription]);
    }

    [self dismissViewControllerAnimated:YES completion:nil];
}

And at this moment app crashes with an error:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (7) must be equal to the number of rows contained in that section before the update (7), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

I have NSFetchedResultsControllerDelegate methods implemented. controllerDidChangeContent: is called after createManagedObject method, but controllerWillChangeContext: is never called and I can't understand why.

- (void)controllerWillChangeContext:(NSFetchedResultsController *)controller {

    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

    switch (type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        case NSFetchedResultsChangeMove:
            break;

        case NSFetchedResultsChangeUpdate:
            break;
    }
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    UITableView *tableView = self.tableView;

    switch (type) {
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        case NSFetchedResultsChangeUpdate:    
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        default:
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {

    [self.tableView endUpdates];
}

How can I resolve this error? Maybe there is a better way to do what I need? (set default name at creation and when saving, set the name from uitextfield)

Upvotes: 1

Views: 240

Answers (2)

pbasdf
pbasdf

Reputation: 21536

You have entered the name of the controllerWillChangeContent method incorrectly: the final word should be content not context:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {

    [self.tableView beginUpdates];
}

Upvotes: 1

Aaron A.
Aaron A.

Reputation: 311

Regarding your comment about a better way to do it, I see you have a property for the managedObjectID of the object that you just created. Is there any reason you don't just have a property for the managed object itself? After you create the new object you would assign it to your local property. Then when you update it, you would just call

[self.newObject setValue:self.nameTextField.text forKey:@"name"];

And then save like you already do (as long as the context you provide in the method [[DVCoreDataManager sharedManager] managedObjectContext] is of type NSMainQueueConcurrencyType. If it is a background context, you'll need to wrap both of those in a -performBlock: or performBlockAndWait:).

This also assumes that your fetched results controller from the presenting viewController is using the same NSManagedObjectContext as your new object viewController.

Upvotes: 0

Related Questions