Good Clean iOS Core Data Stack

Darshan Sonde
YML Innovation Lab
Published in
2 min readOct 11, 2017

I was writing a new iOS 10+ only core data stack and thought I will show how simple the process has become.

DataCoordinator

I really like to have a singleton to manage the core data stack. Traditionally have always called this CoreDataManager. For now lets name it DataCoordinator.

final class DataCoordinator { 
private static var coordinator: DataCoordinator?
public class func sharedInstance() -> DataCoordinator {
if coordinator == nil {
coordinator = DataCoordinator()
}
return coordinator!
}
private init() {
}
}

3 Tier Architecture

TL;DR skip to next section. this is not required anymore.

Now we needed to setup a core data stack. we need to have 2 stacks or 2 contexts.

  1. for reading and displaying data in main thread.
  2. for writing data mostly from api calls and other background processes

This is best achieved by using a 3 tier architecture.

P1 is used to save to persistent store.

C1, C2 are child contexts of P1.

C2 is used to write from API’s. so that saved changes are propagated to C1 fast.

C1 changes are propagated to P1. as its a child context. the propagation is quick and as writing to persistent store happens in another context. operations in C1 is fairly fast. which is the whole goal of setting up the 3 tier architecture. performance!

All this is not required to be setup anymore.

NSPersistentContainer

iOS 10 has introduce a new NSPersistentContainer class which reduced all the boilerplate code of setting up a 3 tier architecture to couple of lines below.

...public var container : NSPersistentContainer
private init() {
container = NSPersistentContainer(name: "Model")
container.loadPersistentStores(completionHandler:{(_, error) in
if let error = error {
fatalError("Unresolved error \(error)")
}
})
}
...

That’s it.

Container provides two ways to read and write.

  1. viewContext property
  2. performBackgroundTask method

Let’s add couple of convenience methods to help in reading and writing from container.

static func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
DataCoordinator.sharedInstance().container.performBackgroundTask(block)
}

static func performViewTask(_ block: @escaping (NSManagedObjectContext) -> Void) { block(DataCoordinator.sharedInstance().container.viewContext)
}

Reading

To read all we have to do is

DataCoordinator.performViewTask { (context) in
}

make sure to use DispatchQueue.main.async or DispatchQueue.main.syncbefore using this method when working from threads.

DispatchQueue.main.async {
DataCoordinator.performViewTask { (context) in
}
}

Writing

To write all we have to do is

DataCoordinator.performBackgroundTask { (context) in
}

You can find the entire code below.

--

--