Quickie: Core Data “Scratchpads”

The Cocoa Core Data documentation makes frequent reference to the fact that Managed Object Contexts (MOCs) are “scratchpads“, in which objects can be moved around willy-nilly without affecting the permanent store. This is all well and good, but it leaves important questions unanswered, such as: “How do you use this fact to implement a ‘cancel’ button?” The answer turns out to be a little more complicated than you might suppose, but can be summed up in one word: Notifications.

The Problem

Let’s say you have a small iPhone app with two view controllers:

  • List, which displays a list of employee objects
  • Employee, which displays an employee’s name and salary

To keep things simple, let’s assume that the List controller brings up a child controller whenever you select an employee name, and that it also has an “add” button, which brings up a blank Employee controller. Finally, let’s assume that Employee controllers are always in “edit” mode, with “cancel” and “save” buttons in their nav bars.

Assuming that this is all built in Core Data, how would you implement the “cancel” button? Pretty easy, you might think: Just assign the Employee controller its own MOC, and then pop the controller if the user selects “cancel”. Presto – the “scratch” MOC disappears, and no changes are committed.

What happens if the user presses “save”? Well, the “scratch” MOC will write its changes to the persistent store — and the List MOC will be blissfully unaware of them. If, for instance, you change an employee’s name with this method, the List controller will display the old name when you return to it. This is not so good.

NSFetchedResultsControllerDelegate

Your first thought might be to have the List controller adopt the NSFetchedResultsControllerDelegate protocol. This is, indeed, necessary, but it is only half the solution. The real problem is that MOCs aren’t notified when the data in their Persistent Store Controller change. Fortunately, MOCs themselves do post notifications when they save data, and other MOCs can capture these notifications, update their managed objects, and then pass messages to their delegates to update the necessary views.

NSManagedObjectContextDidSaveNotification

Here’s a snippet of code demonstrating the key concept – this could be an implementation of the List controller’s add method, invoked when the “add” button is pressed:

- (void)add
{
	// Create "working" MOC
	NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] init];
	moc.persistentStoreCoordinator = self.managedObjectContext.persistentStoreCoordinator;

	// Register "current" MOC for notifications from "working" MOC
	[[NSNotificationCenter defaultCenter] addObserver:managedObjectContext
				 		 selector:@selector(mergeChangesFromContextDidSaveNotification:)
						     name:NSManagedObjectContextDidSaveNotification
						   object:moc];

	// Create a new controller stack
	EmployeeController* e = [[EmployeeController alloc] initWithNibName:@"EmployeeController" bundle:nil];
	UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:e];

	// Prepare the "new" controller
	e.managedObjectContext = moc;
	e.employee = (Employee*) [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:moc];
	e.modal = YES;
	
	// Present the controller stack as a modal view
	[self.navigationController presentModalViewController:nc animated:YES];

	// Clean up
	[nc release];
	[e release];
	[moc release];
}

The key bit of code is the addObserver:selector:name:object: call, which captures notifications posted by the “scratchpad” MOC when and if it commits changes to the persistent store, and merges those changes into the observing MOC (i.e. the List controller’s MOC). The controller can then use delegation (as discussed two weeks ago) to update the list view.

Share and Enjoy:
  • Twitter
  • Facebook
  • Digg
  • Reddit
  • HackerNews
  • del.icio.us
  • Google Bookmarks
  • Slashdot
This entry was posted in iPhone. Bookmark the permalink.

Comments are closed.