Where to Store a Core Data Persistent Store

Where to Store a Core Data Persistent Store

Where do you store the persistent store of your Core Data application? If you are using the Core Data stack Xcode created for you, then chances are that you haven't changed the location of the persistent store. In this tutorial, I show you where you can keep the persistent store of your Core Data application and I highlight the pros and cons of each option.

What Does Apple Do

Before we discuss the options you have, I would like to show you where the persistent store is kept if you use Xcode's Core Data template.

Xcode 7

If we create a project in Xcode 7 and check the Use Core Data checkbox during the setup of the project, we start with an AppDelegate class that contains the necessary code to set up a Core Data stack. The implementation of the persistentStoreCoordinator property shows us where the persistent store is kept. Note that Xcode 7 uses Swift 2, not Swift 3.

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
    // Create the coordinator and store
    let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
    var failureReason = "There was an error creating or loading the application's saved data."
    do {
        try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
    } catch {
        // Report any error we got.
        var dict = [String: AnyObject]()
        dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
        dict[NSLocalizedFailureReasonErrorKey] = failureReason

        dict[NSUnderlyingErrorKey] = error as NSError
        let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
        // Replace this with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
        abort()
    }

    return coordinator
}()

The url constant stores the location of the persistent store. We use the value stored in applicationDocumentsDirectory, a lazy property that stores the location of the Documents directory of the application's data container, and append the name and extension of the persistent store.

let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")

This is what the implementation of the applicationDocumentsDirectory property looks like.

lazy var applicationDocumentsDirectory: NSURL = {
    // The directory the application uses to store the Core Data store file. This code uses a directory named "com.cocoacasts.PersistentStores" in the application's documents Application Support directory.
    let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
    return urls[urls.count-1]
}()

If we run the application and access the managed object context, the persistent store is automatically created at the specified location.

The Core Data persistent store is located in the Documents directory of the application's data container.

Xcode 8

If you create a new project in Xcode 8, the deployment target is automatically set to iOS 10 or higher. If you check the Use Core Data checkbox during the setup of the project, the Core Data stack looks very different. As of iOS 10 and macOS 10.12, the NSPersistentContainer class is in charge of setting up and managing the Core Data stack.

lazy var persistentContainer: NSPersistentContainer = {
    /*
     The persistent container for the application. This implementation
     creates and returns a container, having loaded the store for the
     application to it. This property is optional since there are legitimate
     error conditions that could cause the creation of the store to fail.
    */
    let container = NSPersistentContainer(name: "PersistentStores")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

            /*
             Typical reasons for an error here include:
             * The parent directory does not exist, cannot be created, or disallows writing.
             * The persistent store is not accessible, due to permissions or data protection when the device is locked.
             * The device is out of space.
             * The store could not be migrated to the current model version.
             Check the error message to determine what the actual problem was.
             */
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    return container
}()

We cannot deduce where the persistent store is going to be stored based on the implementation of the persistentContainer property. If you run the application and inspect the data container of the application, you may be surprised that the persistent store isn't located in the Documents directory. Instead, it is located in the Application Support directory, a subdirectory of the Library directory.

The Core Data persistent store is located in the Application Support directory of the application data container.

What Are Your Options?

Last week, we explored the contents of the application's data container on iOS. The data container houses three default directories:

  • Documents
  • Library
  • tmp

Remember that the tmp directory can be purged by the operating system when your application is not running. Therefore, it is unwise to keep the persistent store of your application in the tmp directory.

The location that makes the most sense is the Documents directory. This directory is used to store data that is generated by the user and some of that data is most likely stored in the persistent store of your application. Even though the NSPersistentContainer class doesn't store the persistent store in the Documents directory, it is a valid option. Remember that Xcode's Core Data stack used to keep the persistent store in the Documents directory.

With the introduction of the NSPersistentContainer class in iOS 10, Apple has decided to keep the persistent store in a subdirectory of the Library directory, the Application Support directory. This decision was probably motivated by how the data container of a macOS application is organized. On macOS, the persistent store is stored, by default, in the Application Support directory.

On macOS, the Core Data persistent store is located in the Application Support directory.

Where Should You Store the Persistent Store

The answer to this question is easier than you might think if you consider the characteristics of each of the options we discussed.

tmp

The tmp directory is not a good option. The operating system doesn't guarantee that anything you store in the tmp directory is going to be around the next time your application is run.

If you only need to store data for as long as the application is running, then I recommend using an in-memory persistent store instead. An in-memory persistent store is more performant and the result is virtually identical.

Documents

As I mentioned earlier, the Documents directory is still a good candidate. It is meant to be used to store data that is generated by the user and it is backed up by iTunes and iCloud.

There is one caveat. The contents of the Documents directory is accessible to the user if the application supports file sharing. This means the user can access and tamper with the contents of the persistent store. This is important to keep in mind.

Library

The Application Support directory seems to be the most suitable location for storing the persistent store. If you decide to keep the persistent store in the Library directory, I recommend to at least create a subdirectory for it. This keeps the Library directory uncluttered and organized.

The Caches directory may seem like another option, but I would advise against that. It can be cleared by the operating system and, more importantly, it is not backed up by iTunes and iCloud. The contents of the Library directory are backed up by iTunes and iCloud, with the exception of the Caches directory. That is another important element to consider.

How to Change the Location of the Persistent Store

If you are using the NSPersistentContainer class to set up and manage the Core Data stack, you need to subclass it to modify the location of the persistent store. The method you need to override is the defaultDirectoryURL() method. It specifies the default location of the persistent store on the current platform.

What's Next?

Even though the default location of the persistent store is fine for most applications, it is important to consider your options. You should now have a clear picture of the options you have as well as their pros and cons.