Duplicate NSManagedObject with NSFetchedResultsController

While working on WineLedger for the iPhone I spent a lot of time trying to work out why duplicate ‘ghost’ entries would sometimes appear when merging two NSManagedObjectContexts:

As I inserted a new entry, two copies would appear: One would remain stuck, while the second would correctly update when you edited it. As soon as the app was restarted the ghost entry would miraculously vanish. To add to the confusion, this behaviour would only occur when I used the camera to take a photo via UIImagePickerController.

After a lot of trial and error I finally discovered that the main view was being unloaded from memory as soon as you took a picture. Using the camera causes a huge memory spike as the phone stores the high resolution image.

This shouldn’t be a problem, but as the view loads back into memory the NSFetchedResultsController does a fetch. This initial fetch loads the full set of results (including the freshly inserted object), and then the delegate methods proceed to perform a nice slide animation when the two contexts are merged, inserting the object into the UITableView again.

The fix for this problem is to insert a guard to check to see if the NSFetchedResultsController already has some managed objects before it does a fetch on viewDidLoad.

if (self.fetchedResultsController.fetchedObjects == nil)
{
    NSError * error = nil;
	
    if (![self.fetchedResultsController performFetch:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }
}