Blog

iOS – NSFetchedResultsController example with CoreData manipulation through an NSOperation

11 Feb, 2012
Xebia Background Header Wave

Recently I started working for Xebia and what better way to introduce myself with a nice blogpost and some free code and some explanation to go along with it.
To get you started: here is the code. It’s on GitHub so don’t be afraid to send in suggestions and whatnot.The example I will be talking about today is mostly contained within these classes:

  • XSDAppDelegate
  • XSDFirstViewController

Just make sure to browse the source code a bit before asking questions. 😉
The following picture should give you a general overview of how the example works.

Ios xsdcontroller1 concept sketch

Now for the interesting bits. As can be seen in the picture the example utilizes a number of mechanisms available in the iOS framework. These are CoreData, NSOperation, NSFetchedResultsController and Notification Center.
The NSFetchedResultsController performs a fetch request on the CoreData store. Any changes affecting the NSFetchedResultsControllers NSManagedObjectContext are picked up and delegated to the XSDFirstViewController. The XSDFirstViewController implements the NSFetchedResultsControllerDelegate protocol.
Here’s the implementation of the NSFetchedResultsControllerDelegate. As you can see, nothing too fancy and basically straight from Apple sample code.
[sourcecode language="c"]

  • (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    //Lets the tableview know we’re potentially doing a bunch of updates.
    [self.tableView beginUpdates];
    }
  • (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    //We’re finished updating the tableview’s data.
    [self.tableView endUpdates];
    }
  • (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:UITableViewRowAnimationAutomatic];
    break;
    case NSFetchedResultsChangeDelete:
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    break;
    case NSFetchedResultsChangeUpdate:
    [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    break;
    case NSFetchedResultsChangeMove:
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
    break;
    }
    }
  • (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)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;
    }
    }
    [/sourcecode]
    The NSOperation subclass XSDSyncOperation defines the background work which alters the CoreDate store contents. Key thing to note is that the XSDSyncOperation obtains it’s own NSManagedObjectContext whis uses the same NSPersistentStoreCoordinator as the context used by the NSFetchedResultsController. (Now read that last line again and make sure you understand what I’m saying.)
    An NSPersistentStoreCoordinator can be shared across threads, an NSManagedObjectContext can not. Read the CoreData programming guide for more details on this.
    Now when the XSDSyncOperation saves something to the CoreDate store, the other context needs to be informed about this change. And that’s where the following code snippet comes in, it’s part of the XSDSyncOperation.
    [sourcecode language="c"]
    //Store resulting objects and broadcast the save operation to the syncdelegate.
    [[NSNotificationCenter defaultCenter] addObserver:syncDelegate selector:@selector(syncDidSave:) name:NSManagedObjectContextDidSaveNotification object:context];
    [context save:&error];
    [[NSNotificationCenter defaultCenter] removeObserver:syncDelegate name:NSManagedObjectContextDidSaveNotification object:context];
    [/sourcecode]
    There are a few things to note about the above snippet. First the NSManagedObjectContextDidSaveNotification is a notification every NSManagedObjectContext emits when it receives a save message. Second, the observer syncDelegate with the selector syncDidSave:. The XSDAppDelegate implements a protocol called XSDSyncDelegate. This protocol defines the syncDidSave: message. So when the XSDSyncOperation saves its context, a message syncDidSave: is called on the XSDAppDelegate with one parameter, the NSManagedObjectContextDidSaveNotification triggered by the original save message on the XSDSyncOperation’s context.
    Here’s the implementation of the syncDidSave: method on the XSDAppDelegate:
    [sourcecode language="c"]
  • (void)syncDidSave:(NSNotification *)saveNotification {
    if ([NSThread isMainThread]) {
    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];
    } else {
    [self performSelectorOnMainThread:@selector(syncDidSave:) withObject:saveNotification waitUntilDone:NO];
    }
    }
    [/sourcecode]
    There’s a little bit of logic in play here to switch the processing to the main thread. Once we’re on the main thread the notification is "merged into the main thread’s context. And voila, the context changes, the NSFetchedResultController picks it up and the UI is updated.
Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts