Solving Coredata Concurrency Issues

This is my first article on Medium!

In my iOS app development career using Coredata with web service communication has been an adventurous journey so far.

This becomes even more dreadful when you have to work on the the codebase written by other developer. And it was my first hand experience of storing large data sets downloaded from web services in Coredata with complex relationships to mimic data design of backend enterprise system.

Me and my colleague spent almost 2 days just to find out the reason of intermittent crashes in the application. And imagine the hard deadline by the frustrated client. The reason for the crash was Coredata concurrency. Original developer had used way too many Managed Object Context to fetch and save data and too in multiple threads (Main and background)

In early 2014 we solved the problem by only keeping 2 managed contexts to achieve the results, but it takes greater level of attention to detail to prevent the app from crashes.

The most frustrating part is that these crashes are intermittent and very difficult to reproduce but in XCode 8 you can enable the concurrency debugging assertions by passing -com.apple.CoreData.ConcurrencyDebug 1 to your app on the command line via Xcode’s Scheme Editor.

When you launch your app, you should see a message in the console that tells you that the assertions are now enabled:

CoreData: annotation: Core Data multi-threading assertions enabled.

Legacy: Manual Communication

There is manual way of handling Core data in multithreading environment i.e. creating separate managed context per thread (Main and background). And manually save changes from one context to other. If you have background context downloading a data from web service and front end context displaying data to user, you must notify the front end context when new data has arrived and this is cumbersome and more importantly leave you open for the untraceable errors.

But Apple provides modern way to solve this problem by creating parent-child relationship between the managed contexts.

Modern: Parent — Child communication

The most reliable solution is to have two managed contexts, one for downloading the large datasets from network and other for displaying the data in main thread responsible for updating user interface.

For handling all Coredata operations you can create a helper class called CoreDataStack (Singleton) which you can use throughout the project.

You can declare them as

Here ‘context’ is main context in main queue and ‘backgroundContext’ as its name suggests runs in background context. You can also observe that context is parent of background context. This way the contexts own the thread and is example of ‘Actor’ Design patter, but more on ‘Actor’ later.

Now, how to use them; when you have to download large dataset from network you can use following helper method

You can call performBackgroundBatchOperations with Batch closure to perform heavy operations in background thread. As soon as the closure returns call save() method on backgroundContext which would save the data to mainContext as its parent. Also it sends the notification to parent context which you can then use to update the user interface by using NSFetchedResultsController.

I’ve read many posts regarding same but couldn’t find the example with real life example.

Things gets messy when you have to create the relationship between managed objects owned by managed contexts owning different threads.

For example, you are downloading the photos from web services and saving them in core data.

self.stack.performBackgroundBatchOperation { (workerContext) in
// parse the json returned from service
// create managed object for photo
// assign the relation between photo and album (already fetched but in main managed context)

In this case one way to achieve this is before you parse the json bring the album managed object in workerContext like this

let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: “Pin”)
let predicate = NSPredicate(/* some condition */)
fetchRequest.predicate = predicate
workerContext.fetch(fetchRequest) as! [Album]

No photo and album objects are in same managed contexts and there would be no EXC_BAC_ACCESS exception thrown here.

I’m mobile application, machine learning and artificial intelligence enthusiast and a passionate learner.

You can find me here https://www.linkedin.com/in/aniketghode/

Thank you for reading, hoping to write some more articles soon.

Please share your valuable feedback on how I can improve this.