Moving Core Data Files

Using Core Data with an SQLite database and need to move the location of the store or replace the contents? Resist the temptation to use direct file operations and use the persistent store coordinator to do the job for you.

There are at least a couple of common situations when you may need to move a Core Data store:

  • You decided to add an App Extension and need to move your store into a shared AppGroup directory.
  • You need to populate or reset a store to a default database that you ship in the app bundle.

Avoid direct file operations

Moving the sqlite database with direct file operations is most likely not what you want:

let oldStoreURL: URL = ...
let newStoreURL: URL = ...
do {
  try FileManager.default.moveItem(at: oldStoreURL, to: newStoreURL)
} catch {
  // Handle error
  print("Failed to move from: \(oldStoreURL) to \(newStoreURL)")
}

There are two problems with this. Firstly you still need to tell the Core Data persistent store coordinator to use the new store location. Secondly it assumes that the persistent store is a single file.

Since iOS 7 Core Data uses an SQLite database with Write-Ahead Logging (WAL) journalling by default. In WAL mode Core Data appends transactions to a -wal file and uses a -shm shared memory file in the same location as the main sqlite file.

Core Data Files

Moving just the sqlite file leaves you with potential data loss and an inconsistent store.

Persistent Store Migration

A better way that avoids both of these problems is to have the persistent store coordinator take care of the move.

Note if you are using the new in iOS 10 persistent container it maintains a reference to the persistent store coordinator:

// var persistentContainer: NSPersistentContainer
let psc = persistentContainer.persistentStoreCoordinator

To move the location of a store use the migratePersistentStore(_:to:options:withType:) database migration method. Assuming we have URL’s for the old and new store locations:

let oldStoreURL: URL = ...
let newStoreURL: URL = ...

First ask the persistent store coordinator for the NSPersistentStore at the old URL (you need to have the old store loaded):

if let oldStore = psc.persistentStore(for: oldURL) {

Then try the migration. This operation throws so wrap it in a do-try-catch to handle the error:

do {
  try psc.migratePersistentStore(oldStore, to: newStoreURL, options: nil, withType: NSSQLiteStoreType)
} catch {
  // Handle error
  print("Failed to move from: \(oldStoreURL) to \(newStoreURL)")
}

Note that this does not remove the sqlite files for the old store.

Replace a store

This is almost the same as moving a store. Useful for when you want to reset a store or populate it with a seed database you ship in your application bundle.

let storeURL = ...
let initialStoreURL = ...

do {
  try psc.replacePersistentStore(at: storeURL, destinationOptions: nil,
    withPersistentStoreFrom: initialStoreURL, 
    sourceOptions: nil, ofType: NSSQLiteStoreType)
} catch {
  // Handle error
  print("Failed to replace \(storeURL)")
}