8
Jun
2014
 

The Core Data stack in Swift

by Marcus Zarra

swift-heroWhenever Apple releases a new version of Xcode one of the first things that I do is look at the default templates and see if there are any new or interesting things.

This year, with the release of Swift, there are some pretty radical changes. Yet the Core Data stack initialization code is still the same.

There is nothing wrong with the default template code but there isn’t really anything right about it either. It is far better than it once was but it is still overly verbose and hard to follow.

Therefore, I present my Swift Core Data stack code that I will be using as I grok this language.

saveContext()

func saveContext () {
  var error: NSError? = nil
  let moc = self.managedObjectContext
  if moc == nil {
    return
  }
  if !managedObjectContext.hasChanges {
    return
  }
  if managedObjectContext.save(&error) {
    return
  }

  println("Error saving context: \(error?.localizedDescription)\n\(error?.userInfo)")
  abort()
}

The save method is always the first one that I attack. I strongly dislike having conditions inside of conditions inside of conditions. It is ugly and hard to follow. I much prefer the fail early, fail often design. In my version of the saveContext() we do just that. Every opportunity to fail (or finish) is exercised individually and if it is a failure or finish condition, we return.

All of the logic is on the left side and easy to follow.

managedObjectContext (Version 2)

Next up is the stack itself. Apple creates four variables when we really only use one. It is rare to use persistentStoreCoordinator more than once in the life cycle of an application and even more rare to use managedObjectModel more than once. So why have individual variables for them? This is even more wasteful when you realize that both are accessible from the managedObjectContext. Therefore, I prefer to roll them into one, fairly easy to follow variable.

@lazy var managedObjectContext: NSManagedObjectContext = {
  let modelURL = NSBundle.mainBundle().URLForResource("SwiftTestOne", withExtension: "momd")
  let mom = NSManagedObjectModel(contentsOfURL: modelURL)
  ZAssert(mom != nil, "Error initializing mom from: \(modelURL)")

  let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)

  let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
  let storeURL = (urls[urls.endIndex-1]).URLByAppendingPathComponent("SwiftTestOne.sqlite")

  var error: NSError? = nil

  var store = psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error)
  if (store == nil) {
    println("Failed to load store")
  }
  ZAssert(store != nil, "Unresolved error \(error?.localizedDescription), \(error?.userInfo)\nAttempted to create store at \(storeURL)")

  var managedObjectContext = NSManagedObjectContext()
  managedObjectContext.persistentStoreCoordinator = psc

  return managedObjectContext
}()

Apple’s sample code uses two variables, one that is calculated and then one “private” variable that actually holds the reference. Thanks to lots of feedback from other developers we have a more elegant solution. A lazily loaded var. This simplifies the code quite a bit.

Inside of the getter is where all of the code from the other functions exist. We first grab the URL for the model and then pass that URL into the NSManagedObjectModel. We then test to make sure that the NSManagedObjectModel initialized properly. Right now ZAssert is a global function that is tied to a constant Bool to determine how it reacts to failure. I am still playing with that part so it is not fixed in stone and will get its own blog post once I am happy with it. For now it looks like:

let DEBUG = true

func ZAssert(test: Bool, message: String) {
  if (test) {
    return
  }

  println(message)

  if (!DEBUG) {
    return
  }

  var exception = NSException()
  exception.raise()
}

Note that we are creating mom as a constant. We know this object is not going to change for us so we don’t need to create it as a variable. Having constant objects like this is a pretty cool feature of Swift.

Once we know the NSManagedObjectModel has been initialized properly we can move on to our NSPersistentStoreCoordinator. Creating the NSPersistentStoreCoordinator is just a matter of passing in the initialized NSManagedObjectModel that we just created. We create the NSPersistentStoreCoordinator as another constant. Next we need to load a NSPersistentStore into the NSPersistentStoreCoordinator.

To do that we need to know where the store is going to be saved. We grab all of the possible locations for the documents directory and select the last one from the returned array. We can then append the filename of our store to the end of the NSURL.

With a location we can now ask the NSPersistentStoreCoordinator to add a store for that location. We call the method addPersistentStoreWithType on the NSPersistentStoreCoordinator and we get back either a NSPersistentStore or nothing. If we get nothing back then that is a failure which we check for.

Finally after the NSPersistentStore has been created we can create the actual NSManagedObjectContext. We just initialize the NSManagedObjectContext, hand it the NSPersistentStoreCoordinator and return.

Wrap Up

This has changed about three times already and will probably change again. I am not crazy about how the storeURL is being constructed and will probably clean that up as I go forward. I am certain my ZAssert is going to change.

If you have any suggestions on how to improve, simplify this, please shoot me an email at the usual place. Hopefully we can get down to the infamous five lines of code soon. Right now we are sitting at about 8 (we started around 11)…