Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSFetchedResultsController delegate exception

I have an app which uses a table view to display a list of items form my core data. I am using a remote api and I am updating my content after pulling down the table view - this triggers a call to the API.

Data is retrieved, parsed and inserted/updated into my Core Data.

I am sometimes getting an error after saving my Core Data context... Note that I'm not using multiple threads for this and like I said it doesn't seem to always happen.

I am literally going quite mad. It seems this guy has a similar issue but I'm still unable to fix mine with his solution: CoreData error driving me crazy... CoreData: Serious application error. An exception caught from delegate of NSFetchedResultsController

Here is the full error:

2012-07-31 14:14:47.332 MyApp[2893:11303] 
*** Assertion failure in -[_UITableViewUpdateSupport _setupAnimationsForNewlyInsertedCells], 
/SourceCache/UIKit_Sim/UIKit-1914.84/UITableViewSupport.m:1133
2012-07-31 14:14:47.332 MyApp[2893:11303] CoreData: error: Serious application error.  
An exception was caught from the delegate of NSFetchedResultsController during a call to -
controllerDidChangeContent:.  
Attempt to create two animations for cell with userInfo (null)

UPDATE:

I have a predicate on my fetch request. In order to seem to be deleting objects previously downloaded from API and which are missing from the new JSON result. I am setting a hideFromUser flag, this is saved in my Core Data.

If this flag is YES then it doesn't appear in the table view. But if it's ok then it does. I am also updating info on that managed object should anything be changed. Is it possible that I have an object which was previously set to hide... and is now set to show, and it also had some new data, could this possible trigger a "cell should update" and a "cell should insert" ?

More I think about it less it seems to be relevant.

Here is how I am updating my data:

1) I set all relevant objects of the corresponding type to "hide form user" (NSPredicate ensures they don't show in Table View).

2) I get an NSArray from the JSON data.

3) Looping each item, my createABookOfClass:withJSON: method queries the core data for a book (using an ID from the json dictionary), if it doesn't find it, it creates a new one. Note: at this point the "hide from user flag" is reverted.

4) After all is done, I save.

[[DPLocalStore getInstance] hideFlagItemsOfType:NSStringFromClass([MyFavouriteBook class])];

NSArray * itemsJSON = [data mutableObjectFromJSONData];

[itemsJSON enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) {     
    [[DPLocalStore getInstance] createABookOfClass:[MyFavouriteBook class]
                                          withJSON:obj];
}];

NSError *error = nil;
BOOL didsave = [[DPLocalStore getInstance] save:&error];

Maybe what is happening is a cell containing Object A has been updated, it's update: the hide flag has changed. thus I am getting into a situation where the NSFetchedResultsController's delegate wants to update that cell, and delete it also... since the predicate now doesn't correspond to this object... That sound very likely...

like image 222
Daniel Avatar asked Jul 31 '12 17:07

Daniel


1 Answers

I think I may have found the error, since I changed it I haven't experienced the problem so I am assuming that it's ok.

In my implementation of controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: I had this in the switch statement for NSFetchedResultsChangeMove:

case NSFetchedResultsChangeMove:
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                     withRowAnimation:UITableViewRowAnimationFade];

    // Reloading the section inserts a new row and ensures that titles are updated appropriately.
    [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section]
             withRowAnimation:UITableViewRowAnimationFade];
    break;

As it was used in an older project (which I was not part of), I assumed the entire controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: implementation was okay, since this really is more of a template rather then something you will do anything customised.

In consequence I didn't pay much attention to it until I was reading up about it again on the Apple developer site. And I noticed, in case of a NSFetchedResultsChangeMove you must delete a cell, and insert the cell at the new path. Yet I had implemented without really realising - delete the cell and reload the section !

In effect I should and now have the following:

case NSFetchedResultsChangeMove:
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                     withRowAnimation:UITableViewRowAnimationFade];
    [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                     withRowAnimation:UITableViewRowAnimationFade];
    break;

Like I said I've not had a problem since. Would love to know from anyone what inner workings would be responsible for this - the exception having been "Attempt to create two animations for cell".

Thanks for any interest.

like image 199
Daniel Avatar answered Oct 12 '22 09:10

Daniel