We currently use one managed object context, which we created in the CoreDataManager class. In the application, we pass the managed object context to the objects that need it. This works fine, but there will be times when one managed object context won't cut it.
What happens if you access the same managed object context from different threads? What happens if you pass a managed object from a background thread to the main thread? But let's start with the basics.
Concurrency Basics
Before we explore solutions for using Core Data in a multithreaded environment, we need to know how Core Data behaves on multiple threads. The documentation is very clear about this. Core Data expects to be run on a single thread. Even though that thread doesn't have to be the main thread, Core Data wasn't designed to be used on different threads.
The Core Data team at Apple isn't naive, though. It knows that a persistence framework needs to be accessible from multiple threads. A single thread may be fine for many applications, but more complex applications need a robust, multithreaded persistence framework.
Before I show you how Core Data can be used in a multithreaded environment, I lay out the basic rules for accessing Core Data in a multithreaded application.
Managed Objects
Instances of the NSManagedObject class should never be passed from one thread to another. That's a simple rule you need to respect. If you need to pass a managed object from one thread to another, you use a managed object's objectID property.
The objectID property is of type NSManagedObjectID and uniquely identifies a record in the persistent store. The NSManagedObjectContext class knows what to do when you hand it an NSManagedObjectID instance. The NSManagedObjectContext class defines three methods to work with NSManagedObjectID instances:
object(with:)existingObject(with:)registeredObject(for:)
Each of these methods accepts an instance of the NSManagedObjectID class.
The first method, object(with:), returns a managed object that corresponds with the NSManagedObjectID instance that's passed in. If the managed object context doesn't have a managed object for that object identifier, it asks the persistent store coordinator. This method always returns a managed object.
Know that object(with:) throws an exception if no record can be found for that object identifier. For example, if the application deleted the record corresponding with the object identifier, Core Data is unable to hand your application the corresponding record. The result is an exception.
The existingObject(with:) method behaves similarly. The main difference is that the method throws an error if it can't fetch the managed object corresponding with the object identifier.
The third method, registeredObject(for:), only returns a managed object if the record you're asking for is already registered with the managed object context. In other words, the return value is of type NSManagedObject?. The managed object context doesn't fetch the corresponding record from the persistent store if it can't find it in the managed object context.
The object identifier of a record is similar, but not identical, to the primary key of a database record. It uniquely identifies the record and enables your application to fetch a particular record regardless of what thread the operation is performed on.
If the application asks a managed object context for a managed object with a particular object identifier, the managed object context first looks if a managed object with that object identifier is registered in the managed object context. If there isn't, the managed object is fetched or returned as a fault. We discuss faults later in this series. Don't worry about it for now.

It's important to understand that a managed object context always expects to find a record if you give it an NSManagedObjectID instance. That's why object(with:) returns an object of type NSManagedObject, not NSManagedObject?.
Managed Object Context
Creating an NSManagedObjectContext instance is a cheap operation. You should never share a managed object context across threads. This is a hard rule you shouldn't break. The NSManagedObjectContext class isn't thread safe. Plain and simple.
Persistent Store Coordinator
Even though the NSPersistentStoreCoordinator class isn't thread safe either, the class knows how to lock itself if multiple managed object contexts request access, even if these managed object contexts live and operate on different threads.
It's fine to use a single persistent store coordinator that's accessed by multiple managed object contexts from different threads. This makes Core Data concurrency a little bit easier.
Managing Concurrency
Core Data has come a long way and it used to be a nightmare to use Core Data in a multithreaded environment. You still need to be careful when using Core Data on multiple threads, but it's become easier since iOS 6. Apple added a number of useful APIs to the Core Data framework to make your life as a developer easier.
Updating the Core Data Stack
Theory
Before ending this episode, I want to talk about parent and child managed object contexts, a topic I briefly mentioned in the introduction of this series.
Complex applications that heavily rely on Core Data can run into problems if changes of the managed object context are written to the persistent store on the main thread. Even on modern devices, such operations can result in the main thread being blocked. Because the main thread is also used to update the user interface of your application, the user experiences this as the application freezing up for a moment.
This can be avoided by slightly modifying the Core Data stack of the application. The approach I mostly use looks something like this.

The managed object context linked to the persistent store coordinator isn't associated with the main thread. Instead, it performs its work on a private queue, not on the main queue. When the private managed object context saves its changes, the write operation is performed on that private queue in the background.

The private managed object context has a child managed object context, which serves as the main managed object context of the application. The concept of parent-child managed object contexts is key in this scenario.

In most scenarios, a managed object context is associated with a persistent store coordinator. When such a managed object context saves its changes, it pushes them to the persistent store coordinator. The persistent store coordinator pushes the changes to the persistent store.

A child managed object context doesn't have a reference to a persistent store coordinator. Instead, it keeps a reference to another managed object context, a parent managed object context.
When a child managed object context saves its changes, it pushes them to the parent managed object context. In other words, when a child managed object context saves its changes, the persistent store coordinator is unaware of the save operation. It's only when the parent managed object context performs a save operation that the changes are pushed to the persistent store coordinator and subsequently to the persistent store.

Because no write operations (no disk I/O) are performed when a child managed object context saves its changes, pushing changes from a child managed object context to its parent is fast and efficient. This also means that the queue on which the operation is performed isn't blocked by a write operation. That's why the main managed object context of the application is the child managed object context of a managed object context that operates in the background on a private queue.
Practice
It's time to put this into practice by updating the CoreDataManager class. In the next episode, we refactor the CoreDataManagerclass to make it more suitable for use in a multithreaded environment.