I have a parent-child Core Data relationship set up in my iPhone app. I have a Manufacturer object and a Car object. It is a to-many relationship with the Manufacturer being the owner. The main view is a Table View containing the Manufacturers. The detail view is another Table View with the different types of cars. I have been using Tim Roadley’s Core Data Tutorial as the base. This tutorial uses Stanford’s Core Data Table View Library for the table views.
Adding Cars and Manufacturers gives me no problem, but when I go in and delete with multiple cars in the table view I get this error:
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:1037
2012-07-29 23:39:33.561 App [16368:c07] * 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 (0) must be equal to the number of rows contained in that section before the update (2), 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).’
If I delete the only car, it works fine until I try to add a new car, when I get this error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Illegal attempt to establish a relationship 'manufacturer' between objects in different contexts (source = <Car: 0x6d96da0> (entity: Car; id: 0x6d8a3c0 <x-coredata:///Car/tC78E17EB-1D68-4998-8C4D-6D1199CE253F4> ; data: {
dateAdded = nil;
manufacturer = nil;
carName = new;
}) , destination = <Manufacturer: 0x6bb1f40> (entity: Manufacturer; id: 0x6d87340 <x-coredata://2E8DDF34-B01A-4203-A53E-73DBE6A2F976/Garden/p6> ; data: <fault>))'
Here is my editing method:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.tableView beginUpdates];
Plant *plantToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(@"Deleting plant '%@'", plantToDelete.plantName);
[self.managedObjectContext deleteObject:plantToDelete];
[self.managedObjectContext save:nil];
//delete empty tableview row
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
NSLog(@"Before performFetch...");
[self performFetch];
NSLog(@"After performFetch...");
[self.tableView endUpdates];
}
}
The performFetch method is contained in the previously mentioned CoreDataTableViewController files. For your convenience, here it is:
(void)performFetch
{
_debug = YES;
if (self.fetchedResultsController) {
if (self.fetchedResultsController.fetchRequest.predicate) {
if (self.debug) NSLog(@"[%@ %@] fetching %@ with predicate: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate);
} else {
if (self.debug) NSLog(@"[%@ %@] fetching all %@ (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName);
}
NSError *error;
[self.fetchedResultsController performFetch:&error];
if (error) NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]);
} else {
if (self.debug) NSLog(@"[%@ %@] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
}
[self.tableView reloadData];
}
According to other questions, I am doing this correctly by using beginUpdates and endUpdates. This is a puzzling error. Thanks for your help.
I am not sure why you are performing the fetch again, if an object is removed from the context, the fetched results controller is aware of that change already. I think the main problem you have is calling perform fetch in the middle of processing updates to the table. If you comment that line out, does it still have the error?
Additionally, the following may or may not be another part of the the problem as this is where you are differing from my own code:
I have not seen the begin/end edits calls in tableView:CommitEditingStyle: before. My own process in that method generally deletes the object without any concern for the table row. The table rows are reconciled in the fetchedResultController delegate methods like so:
As long as the number of rows matches the number of fetched objects after all that, you should not have that error.