One of the developer-side enhancements included in iPhone OS 3.0 was Core Data. Core Data is basically an ORM layer, which one could either take or leave; it’s nice to avoid writing all that SQL, but you’re always taking on a lot of baggage by adopting a big ol’ framework like that. It does, however, offer at least one feature that strikes me as really neat: NSFetchedResultsControllers
. I explain more below.
Update Messages
The ostensible purpose of NSFetchedResultsController
is to “efficiently manage the results returned from a Core Data fetch request to provide data for a UITableView object”. To which I say: “Meh”. The really interesting feature of this class is its delegate
property. If you set this property to an object which adopts the NSFetchedResultsControllerDelegate
protocol, that delegate will receive messages when changes are made (note: made, not just saved) to the managed object context from which results were fetched.
This is something I would classify as “astoundingly useful”, since it obviates the need for the “triple updating” (of Core Data model, local object model, and view) that otherwise pops up repeatedly in view controllers.
Example
These snippets of code are taken from the iPhone Core Data “Locations” tutorial, after it has been modified (per the suggestion in the tutorial’s “Next Steps” section) to use an NSFetchedResultsController
. (Note that I set the controller’s delegate, and added the delegate methods, to the tutorial’s RootViewController
class.)
- (void)addEvent
{
// Get location
CLLocation* location = [locationManager location];
if (!location) return;
// Create and initialize a new instance of the Event entity
Event* event = (Event*) [NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:managedObjectContext];
// Configure Event
CLLocationCoordinate2D coordinate = [location coordinate];
[event setLatitude:[NSNumber numberWithDouble:coordinate.latitude]];
[event setLongitude:[NSNumber numberWithDouble:coordinate.longitude]];
[event setCreationDate:[NSDate date]];
// Save
NSError* error;
if (![managedObjectContext save:&error])
{
// Handle the error.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1);
}
}
- (void)tableView:(UITableView*)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath*)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
// Delete the managed object at the given index path.
NSManagedObject* eventToDelete = [resultsController objectAtIndexPath:indexPath];
[managedObjectContext deleteObject:eventToDelete];
// Commit the change.
NSError* error;
if (![managedObjectContext save:&error])
{
// Handle the error.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1);
}
}
}
#pragma mark fetched results controller delegate methods
- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller
{
[self.tableView beginUpdates];
}
- (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];
[tableView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController*)controller
{
[self.tableView endUpdates];
}
As you can see, insertions and deletions now only directly affect the Core Data model; changes to this model trigger updates to the table view through the fetched results controller delegate.
Pingback: Things that were not immediately obvious to me » Blog Archive » Table Updates & Scrolling
Pingback: Things that were not immediately obvious to me » Blog Archive » Question Time
Pingback: Things that were not immediately obvious to me » Blog Archive » Core Data Gotcha
Pingback: Things that were not immediately obvious to me » Blog Archive » NSFetchedResultsController pitfall