A Guide to Core Data Concurrency

The iOS ethos of instant responsive UI elements means putting as much work as
possible in background threads and as little work in the main thread. For most
cases we are fine with using an NSOperationQueue or GCD, but getting
concurrency to work in Core Data sometimes feels more like black magic than
science. This post intends to demystify concurrency and offer two ways to go
about it.

Setup one

Private queue context and main queue context off of single persistent store
coordinator

In this setup we will make two NSManagedObjectContext instances one with
concurrency type NSMainQueueConcurrencyType and the other with type
NSPrivateQueueConcurrencyType. We will attach ourself to the
NSManagedObjectContextDidSaveNotification to propagate saves. The power of
this setup is simplicty. Use this setup on applications which do not require
running core data on operation queues. This stack is ideal for an application
which needs a context to do some background work and will mostly be using the
main queue context to display information.

One of the great advantages of this type of core data stack is it allows us to
make great use of NSFetchedResultsController. An example of this is parsing
JSON from a web service into a
core data object as a background operation and then using the fetched results
controller to indicate when said object has changed and updating the UI as a
result. For simple applications this stack is headache free and flexible.

Setup two

The throwaway main queue context backed by a private queue context

In this setup we will have only one NSManagedObjectContext which will stay
with us for the life time of the app. This will be a private queue context which
we will use to create child main queue contexts from. This allows us to spend as
much time in the background and only when we need to do UI work do we create a
new main queue context. This is a slightly more complex stack, and should be
used when your application needs to run core data on multiple background
threads. This setup relies on having a single private queue context which will
serve as a parent to freshly made contexts. Use this stack when you want to run
core data on an operation queue.

Starting from our base core data setup we add the following to TBCoreDataStore.h:

Here we have no need to observe save notifications as any saves on the created
main queue context will bubble up to its parent the
defaultPrivateQueueContext. This approach is very robust and spends the least
possible time on the main queue. The downside is that it we cannot use the
NSFetchedResultsController out of the box though we could cobble our own
version using the different notifications sent out by core data. The power of
this stack is its flexibility. You can spin up new managed object contexts
safely inside an NSOperation subclass to do some heavy lifting or you have the
option of just calling the defaultPrivateQueueContext. Use this setup if you
need full control over core data.

Lets say we have a really big database (20k+ objects) and we want to do a
complex fetch, the best way to go about this is to first use the background
queue to fetch the NSManagedObjectIDs and then hop on the main queue and call
-objectWithID on the results. This is how we should always be passing around
managed objects between threads.

In this scenario we need to update our UI with a bunch of MyEntity managed
objects. For efficiency’s sake we perform the costly fetch in the background
and set the result type to be NSManagedObjectIDResultType which will return
NSManagedObjectIDs. We then create a new mainQueueContext and get each
managed object from the cache by using [mainQueueContext
objectWithID:managedObjectID]. These objects are then safe to use on the main
thread.

If your fetch is not too intensive we can just perform it on your new main queue
context. If we want to use this stack I recommend making this snippet:

Caveats

When performing extremely intensive fetch operations (10+ seconds) in the
background thread and simultaneously needing to perform operations on another
thread we will run into blockage. To prevent this from happening we should
perform this operation an an entirely new context linked to a entirely new PSC.
This will ensure that the operation stays in the background.

Useful utility methods

An extremely useful little one liner is the ability to turn an
NSManagedObjectID into a string. we can use this to store the ID in the user
defaults.

With these two methods we have an easy way to build a cache on disk by using a
plist. This is useful for saving a list of managed objects which need to be
updated or maybe deleted between app launches.

Creating a managed object is a pain, so here is a little method which will make
your life better:

What’s next

Lets recap our two core data stacks. We have setup 1, which is simple and
provides you with the ability to perform background fetches and saves. While
most applications will only require you to handle two contexts, a private queue
and a main queue context, there are situations where being able to spawn
contexts at will becomes important. This is where setup 2 comes in. With this
setup you will be making new contexts which are children of a single parent
private queue context. This setup shines when combined with NSOperation
subclasses which need to communicate with core data.

Have you ever wanted to to program without nil?
Learn about Haskell's Maybe data type
in our new book
Maybe Haskell
to start living in a null-free world.
You'll find this concept useful whether you program
in Haskell, Scala, OCaml,
or are a heavy user of Swift's Optionals.