Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Core Data Progressive Migration
// The MIT License (MIT)
//
// Copyright (c) 2016 Alexander Grebenyuk (github.com/kean).
import Foundation
import CoreData
enum MigrationError: ErrorProtocol {
case IncompatibleModels
}
// moms: [mom_v1, mom_v2, ... , mom_vN]
func migrateStore(at storeURL: URL, moms: [NSManagedObjectModel]) throws {
let idx = try indexOfCompatibleMom(at: storeURL, moms: moms)
let remaining = moms.suffix(from: (idx + 1))
guard remaining.count > 0 else {
return // migration not necessary
}
_ = try remaining.reduce(moms[idx]) { smom, dmom in
try migrateStore(at: storeURL, from: smom, to: dmom)
return dmom
}
}
func indexOfCompatibleMom(at storeURL: URL, moms: [NSManagedObjectModel]) throws -> Int {
let meta = try NSPersistentStoreCoordinator.metadataForPersistentStore(ofType: NSSQLiteStoreType, at: storeURL)
guard let idx = moms.index(where: { $0.isConfiguration(withName: nil, compatibleWithStoreMetadata: meta) }) else {
throw MigrationError.IncompatibleModels
}
return idx
}
func migrateStore(at storeURL: URL, from smom: NSManagedObjectModel, to dmom: NSManagedObjectModel) throws {
// Prepare temp directory
let dir = try URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString)
try FileManager.default().createDirectory(at: dir, withIntermediateDirectories: true, attributes: nil)
defer {
_ = try? FileManager.default().removeItem(at: dir)
}
// Perform migration
let mapping = try findMapping(from: smom, to: dmom)
let destURL = try dir.appendingPathComponent(storeURL.lastPathComponent!)
let manager = NSMigrationManager(sourceModel: smom, destinationModel: dmom)
try autoreleasepool {
try manager.migrateStore(
from: storeURL,
sourceType: NSSQLiteStoreType,
options: nil,
with: mapping,
toDestinationURL: destURL,
destinationType: NSSQLiteStoreType,
destinationOptions: nil
)
}
// Replace source store
let psc = NSPersistentStoreCoordinator(managedObjectModel: dmom)
try psc.replacePersistentStore(
at: storeURL,
destinationOptions: nil,
withPersistentStoreFrom: destURL,
sourceOptions: nil,
ofType: NSSQLiteStoreType
)
}
func findMapping(from smom: NSManagedObjectModel, to dmom: NSManagedObjectModel) throws -> NSMappingModel {
if let mapping = NSMappingModel(from: Bundle.allBundles(), forSourceModel: smom, destinationModel: dmom) {
return mapping // found custom mapping
}
return try NSMappingModel.inferredMappingModel(forSourceModel: smom, destinationModel: dmom)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment