Pitfalls of NSFetchedResultsController

Published on February 9, 2010

I have recently used one of the most useful framework in iPhone 3.0 – CoreData.

There are many guides on using CoreData, such as from cocoadevcentral or Apple’s guide. But what I found lacking is that there was no discussion on the pitfalls of using CoreData, or its view controller, NSFetchedResultsController.

I learnt the pitfalls along the way, and there are 3 particular pitfalls that I would like to share with developers while developing txeet for iPhone.


1. Performance hit with predicate using one-to-many relationship

When using NSFetchedResultsController, we would often form our predicate (like SQL) to retrieve some table rows.

If you were to use a predicate that involves transversing a one-to-many relationship, the performance could be slowed down tremendously (as slow as 30 sec to run, or even crash!). Take for example a Template model that has a one-to-many relationship tags to Tag model:

coredata pitfall code1 

The predicate above would require transversing to each Tag to find ‘love’. This is computationally very expensive.

The solution to this is to avoid transversing relationship. A faster way that CoreData could execute is to access the properties/attributes. For the above example, what I did is to add another attribute tagsAsAttribute to Template model. This property would store the tag names in a delimited format such as “;love;jokes;quotes;”. The predicate would then be changed to:

coredata pitfall code2

Note: This is not the best way to design the data model, as tagsAsAttribute has a dependency and is redundant.


2. User-driven updates

If you read the Apple’s guide to NSFetchedResultsControllerDelegate, please note of the section User-Driven Updates.

In short, if you have user-driven updates, you should write the following as the first line in every (only one is shown below) of the NSFetchedResultsControllerDelegate delegate methods:

coredata pitfall code3

And when there is a user-driven update, set the isStillUpdating boolean to true, and set false when the update is completed.

User-driven updates could be re-ordering of table rows, or inserting of objects.


3. abort() should NEVER be used in release

If you have used the sample code for CodeData, there is a line of code which is there only for testing environment. In the comments, it warned developers that abort() should not be used in shipping application..

But I didn’t read the comments..

Apparently, certain devices will occasionally have error when running certain CoreData methods. Eg. NSManagedObjectContext’s save. When there is an error, we could optionally handle the error, but we should NEVER abort and exit the app.