Drop Database Migration in Core Data

Core Data supports migrating persistent stores (databases) from one model (schema) to the next without losing the existing content. It involves keeping versions of your model and the required mappings between them.

However if you have a REST based app, then you are probably fine with not having to migrate the user's content as you can just fetch it all again from the server. You can do this in a surprisingly simple way (just make sure you do it before you create the NSPersistentStoreCoordinator otherwise the mismatch will abort your app).

  1. Check if the the existing .sqlite file is compatible with the app's Core Data model. If not, delete the file.
  2. Create a NSPersistentStoreCoordinator using the path to the sqlite file as usual.

The check to see if the model is compatible is exactly the same check that would start a Core Data migration, but our deletion ensures that a migration is not necessary.

Here's how to do it in Swift 2:

func createPersistentStoreCoordinator() -> NSPersistentStoreCoordinator {
  let fileManager = NSFileManager.defaultManager()
  let documentsDirectory = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last!
  let storeURL = documentsDirectory.URLByAppendingPathComponent("MyApp.sqlite")

  let modelURL = NSBundle.mainBundle().URLForResource("MyApp", withExtension: "momd")!
  let model = NSManagedObjectModel(contentsOfURL: modelURL)!

  if (fileManager.fileExistsAtPath(storeURL.path!) && !isStoreCompatible(storeURL, withModel: model)) {
      NSLog("Core data store is incompatible with model. Deleting store.")
      do {
          try fileManager.removeItemAtURL(storeURL)
      } catch {
          NSLog("Failed to delete incompatible store, carrying on anyway.")
      }
  }

  let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
  do {
      try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil)
  } catch {
      NSLog("Failed to initialize MyApp core data")
      abort()
  }

  return coordinator
}

private func isStoreCompatible(storeURL: NSURL, withModel model: NSManagedObjectModel) -> Bool {
    var isCompatible = false
    do {
        let storeMetadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(NSSQLiteStoreType, URL: storeURL, options: nil)
        isCompatible = model.isConfiguration(nil, compatibleWithStoreMetadata: storeMetadata)
    } catch {
        NSLog("Failed to check if core data store is compatible, assuming incompatible")
    }

    return isCompatible
}

Now you can make all the changes you like to your model without having to do any versioning! Of course if you keep making changes to the model schema with every release then you lose out on all the performance benefits of using core data as a cache in the first place.

View Comments
Navigation