title: NSFetchedResultsController: Fetch in a background thread
body: <p>I have a more or less basic <code>UITableViewController</code> with a <code>NSFetchedResultsController</code>. The <code>UITableViewController</code> is pushed onto the <code>navigationController's</code> stack. But the push animation isn't smooth because the fetch of <code>NSFetchedResultsController</code> is performed on the main thread, and therefore blocks the UI.</p>
  
  <p>My question is: How can I perform the fetch of the <code>NSFetchedResultsController</code> in a background thread to keep the animation smooth? </p>
  
  <p>The <code>NSFetchedResultsController</code> and the delegate methods look like this:</p>
  
  <pre><code>- (NSFetchedResultsController *)fetchedResultsController
  {
      if (_fetchedResultsController != nil) {
          return _fetchedResultsController;
      }
  
      NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
      // Edit the entity name as appropriate.
      NSEntityDescription *entity = [NSEntityDescription entityForName:@"GPGrade" inManagedObjectContext:self.managedObjectContext];
      [fetchRequest setEntity:entity];
  
      // Set the batch size to a suitable number.
      [fetchRequest setFetchBatchSize:20];
  
      //Set predicate
      NSPredicate *predicate = [NSPredicate predicateWithFormat:@"parent == %@", self.subject];
      [fetchRequest setPredicate:predicate];
  
  
      // Edit the sort key as appropriate.
      NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
      NSArray *sortDescriptors = @[sortDescriptor];
  
      [fetchRequest setSortDescriptors:sortDescriptors];
  
      // Edit the section name key path and cache name if appropriate.
      // nil for section name key path means "no sections".
      NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"SubjectMaster"];
      aFetchedResultsController.delegate = self;
      self.fetchedResultsController = aFetchedResultsController;
  
      NSError *error = nil;
      if (![self.fetchedResultsController performFetch:&amp;error]) {
          // Replace this implementation with code to handle the error appropriately.
          // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
          NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
          abort();
      }
  
      return _fetchedResultsController;
  }
  
  - (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
  {    
      UITableView *tableView = self.tableView;
  
      switch(type) {
          case NSFetchedResultsChangeInsert:
              [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationTop];
              break;
  
          case NSFetchedResultsChangeDelete:
              [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];
              break;
  
          case NSFetchedResultsChangeUpdate:
              //[self configureCell:(GPSubjectOverviewListCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
              break;
  
          case NSFetchedResultsChangeMove:
              [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
              [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
              break;
      }
  }
  
  - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
  {
      [self.tableView endUpdates];
  }
  </code></pre>