title: Assertion failure in -[UITableView _endCellAnimationsWithContext:] with NSFetchedResultsController
body: <p>I have 2 managed object contexts: (1) created as <code>NSMainQueueConcurrencyType</code> that is used by the UI/main thread and (2) created as <code>NSPrivateQueueConcurrencyType</code> that is used by the networking. Both of these contexts go to the persistent store (i.e., I'm not using parent/child contexts).</p>
  
  <p>For the view controller, I'm using a <code>UITableViewController</code> with a <code>NSFetchedResultsController</code> that uses the 1st UI managed object context.</p>
  
  <p>I am merging changes from the 2nd managed object context into the 1st context by observing the <code>NSManagedObjectContextDidSaveNotification</code>.</p>
  
  <p>The app works fine until it processes a network response that causes a new object to be inserted <strong>and</strong> an existing object to be deleted in the 2nd context. When the 2nd context is saved, the <code>NSManagedObjectContextDidSaveNotification</code> fires and the changes are merged into the 1st context. The delegate methods of <code>NSFetchedResultsController</code> are invoked and a new row is added to the table, but the row representing the deleted object is *<em>not</em> removed.</p>
  
  <p>If I attempt other actions on the table view, like reloading the table or updating other objects, I get this assert in the console log:</p>
  
  <pre><code>*** Assertion failure in -[UITableView _endCellAnimationsWithContext:],
  /SourceCache/UIKit/UIKit-2380.17/UITableView.m:1070
  
  CoreData: error: Serious application error.  An exception was caught
  from the delegate of NSFetchedResultsController during a call to -
  controllerDidChangeContent:.  Invalid update: invalid number of rows in
  section 0.  The number of rows contained in an existing section after the
  update (6) 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 (6 inserted, 6 deleted) and plus or minus the number of rows
  moved into or out of that section (0 moved in, 0 moved out). with 
  userInfo (null)
  </code></pre>
  
  <p>Typically, you get this error if you've forgotten to update your model objects when using <code>UITableView</code>'s batch update methods, but in this case, <code>NSFetchedResultsController</code> is doing all work. My delegate methods are boilerplate:</p>
  
  <pre><code>- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
  {
    [self.tableView beginUpdates];
  }
  
  - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id &lt;NSFetchedResultsSectionInfo&gt;)sectionInfo
             atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
  {
    switch(type) {
          case NSFetchedResultsChangeInsert:
              [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
              break;
  
          case NSFetchedResultsChangeDelete:
              [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
              break;
      }
  }
  
  - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
         atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
        newIndexPath:(NSIndexPath *)newIndexPath
  {
    NSLog(@"    didChangeObject type=%d indexPath=%@ newIndexPath=%@", type, indexPath, newIndexPath);
  
    UITableView *tableView = self.tableView;
  
      switch(type) {
          case NSFetchedResultsChangeInsert:
              [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
              break;
  
          case NSFetchedResultsChangeDelete:
              [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
              break;
  
          case NSFetchedResultsChangeUpdate:
              [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
              break;
  
          case NSFetchedResultsChangeMove:
              [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
              [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
              break;
      }
  }
  
  - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
  {
    [self.tableView endUpdates];
  }
  </code></pre>
  
  <p>My UITableViewDataSource <code>tableView:cellForRowAtIndexPath</code> method is:</p>
  
  <pre><code>- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  {
    static NSString *CellIdentifier = @"Cell";
  
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }
  
    [self configureCell:cell atIndexPath:indexPath];
  
    return cell;
  }
  </code></pre>
  
  <p>And <code>configureCell:</code> is:</p>
  
  <pre><code>- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
  {
    Event *event = (Event *)[self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = [[event valueForKey:@"timeStamp"] description];
  
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Owner"];
    NSArray *objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
    cell.detailTextLabel.text = [objects.lastObject name]; // simplified
  }
  </code></pre>