Bugs of NSFetchedResultsControllerDelegate Template Code

This is the template code of controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: given by Apple:

- (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:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

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

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

There are two bugs in the NSFetchedResultsChangeUpdate case:

  1. newIndexPath may be non-nil and different from indexPath, for example, when some rows are deleted or inserted while some other rows are updated in the same session. In these cases, one should use the old indexPath to locate the table cell but use the newIndexPath to fetch the object from the NSFetchedResultsController because controller:didChange… and table view is being updated. So the correct code should be:

    [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:(newIndexPath ? newIndexPath : indexPath)];
    
  2. With the code above, you have correctly updated the table cell. However, the cell on screen is still displayed as before. That’s because even though you’ve re-configured the cell with updated data, you’ve not refreshed it. To force a refresh, you need to call the table cell’s setNeedsLayout or/and setNeedsDisplay depending on your cell’s implementation. Refer to Stack Overflow for the discussion. You can also resort to reloadRowsAtIndexPaths:withRowAnimation: of course, albeit it seems a bit overkill to me.