This is a technical post about the work that has gone into bringing you Trident v1.4.

(v1.4 is not actually released yet! - waiting for review)

v1.4 was not a release for purely features, v1.3 had some serious bugs as I announced earlier. Fixing these bugs involved auditing and rewriting huge parts of the app.

Apples Core Data (an ‘object graph management’ tool that can persist data to SQLite) is excellent. Core Data gives you a great set of tools to build your App with; a data modeling tool, controller classes like NSFetchedResultsController - which make it easy to observe changes in your dataset, a query language with NSPredicate, I could go on. But one thing Core Data doesn’t do well is thread easily. Well it does do threading, but you have to understand the rules. Starting a Core Data project requires knowing those rules and I think Apple could do much better at clearly outlining those rules in their documentation. Luckily, as I was writing Trident an excellent book on Core Data was being written which explains those rules.

Core Data Threading rules

  • Only access and set properties of ManagedObjects on the thread of the context that owns it (typically using performBlock).
  • Avoid use of nested performBlockAndWait to prevent deadlock conditions.
  • It’s safe to pass ManagedObjects across threads, but only objectID is safe to access.
  • Use objectID to find a ManagedObject from a different Context.
  • Use the URIRepresentation property of objectID to fetch an object across different Persistent Stores.

If you’re not certain your app is following those rules, there’s a great debug mode now available for iOS - read this excellent post on how to enable it.

Even if you’re following these rules, threading Core Data requires choosing a certain ‘stack’ to enable importing data in a background thread, and observe those changes on the main thread. Your stack can make the difference between a slow app and a fast one. There are many options of possible ‘stacks’ you could build - but the choice is required for you to take (there is no default Core Data stack!). You will find many references on the internet about the Confinement stack for Core Data - this is now deprecated in iOS 9 and should not be used for new projects. Apple recommends a parent/child context stack - this is the typical stack I think many people use for modern Core Data projects. Apple also recommends another stack option using two NSPersistentStoreCoordinator - this is the approach Trident v1.4 uses to import data.

You’re probably wondering, why did I choose a two NSPersistentStoreCoordinator stack, and what is it? Trident imports lots of data in the background - I wanted to find the best way to do this. The two NSPersistentStoreCoordinator stack involves creating two NSManagedObjectContexts, one with the MainQueueConcurrencyType and other other with PrivateQueueConcurrencyType - each context connects to their own independent NSPersistentStoreCoordinator. The reason why this approach is great is because in typical singular coordinator setup, only one context can access the coordinator at a time - this creates contention at the coordinator level. Creating two coordinators defers that to the SQLite level, which is single writer multi reader. This means the main thread context is free to read the SQLite even during a large update is happening. The Core Data book I mentioned goes into much more detail than I have explained here, particularly about the advantages and disadvantages of this approach and how to set it up.

I hope reading this helps you if you too are suffering from Core Data problems!