From fcda83e6acd36f3b480e15829eb6667ce3a8806c Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Mon, 15 Jun 2015 01:31:03 +0800 Subject: [PATCH] incomplete migration prototype methods --- CoreStore/Migrating/DataStack+Migration.swift | 132 +++++++++++++++++- CoreStore/NSError+CoreStore.swift | 15 ++ 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/CoreStore/Migrating/DataStack+Migration.swift b/CoreStore/Migrating/DataStack+Migration.swift index 2aa8386..54c2ffd 100644 --- a/CoreStore/Migrating/DataStack+Migration.swift +++ b/CoreStore/Migrating/DataStack+Migration.swift @@ -99,6 +99,136 @@ public extension DataStack { ) } + /** + EXPERIMENTAL + */ + public func upgradeSQLiteStoreIfNeeded(fileName: String, configuration: String? = nil, completion: (PersistentStoreResult) -> Void) { + + self.upgradeSQLiteStoreIfNeeded( + fileURL: applicationSupportDirectory.URLByAppendingPathComponent( + fileName, + isDirectory: false + ), + configuration: configuration, + completion: completion + ) + } + + /** + EXPERIMENTAL + */ + public func upgradeSQLiteStoreIfNeeded(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, completion: (PersistentStoreResult) -> Void) { + + var metadataError: NSError? + let metadata: [NSObject: AnyObject]! = NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( + NSSQLiteStoreType, + URL: fileURL, + error: &metadataError + ) + if metadata == nil { + + CoreStore.handleError( + metadataError ?? NSError(coreStoreErrorCode: .UnknownError), + "Failed to load SQLite <\(NSPersistentStore.self)> metadata at \"\(fileURL)\".") + + GCDQueue.Main.async { + + // TODO: inspect valid errors for metadataForPersistentStoreOfType() + completion(PersistentStoreResult(.PersistentStoreNotFound)) + } + return + } + + let coordinator = self.coordinator; + if let store = coordinator.persistentStoreForURL(fileURL) { + + let isExistingStoreAutomigrating = ((store.options?[NSMigratePersistentStoresAutomaticallyOption] as? Bool) ?? false) + + if store.type == NSSQLiteStoreType + && isExistingStoreAutomigrating + && store.configurationName == (configuration ?? Into.defaultConfigurationName) { + + GCDQueue.Main.async { + + completion(PersistentStoreResult(store)) + } + return + } + + CoreStore.handleError( + NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL), + "Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\" because a different <\(NSPersistentStore.self)> at that URL already exists.") + + GCDQueue.Main.async { + + completion(PersistentStoreResult(.DifferentPersistentStoreExistsAtURL)) + } + return + } + + let managedObjectModel = self.coordinator.managedObjectModel + let migrationManager = NSMigrationManager( + sourceModel: NSManagedObjectModel( + byMergingModels: [managedObjectModel], + forStoreMetadata: metadata! + )!, + destinationModel: managedObjectModel + ) + + var mappingModel: NSMappingModel! = NSMappingModel( + fromBundles: nil, // TODO: parametize + forSourceModel: migrationManager.sourceModel, + destinationModel: migrationManager.destinationModel + ) + var modelError: NSError? + if mappingModel == nil { + + mappingModel = NSMappingModel.inferredMappingModelForSourceModel( + migrationManager.sourceModel, + destinationModel: migrationManager.destinationModel, + error: &modelError + ) + } + if mappingModel == nil { + + CoreStore.handleError( + NSError(coreStoreErrorCode: .UnknownError), + "Failed to load an <\(NSMappingModel.self)> for migration from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\".") + + GCDQueue.Main.async { + + completion(PersistentStoreResult(.MappingModelNotFound)) + } + return + } + + let temporaryFileURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)!.URLByAppendingPathComponent(NSProcessInfo().globallyUniqueString) + + var migrationError: NSError? + if !migrationManager.migrateStoreFromURL( + fileURL, + type: NSSQLiteStoreType, + options: nil, + withMappingModel: mappingModel, + toDestinationURL: temporaryFileURL, + destinationType: NSSQLiteStoreType, + destinationOptions: nil, + error: &migrationError + ) { + + CoreStore.handleError( + migrationError ?? NSError(coreStoreErrorCode: .UnknownError), + "Failed to prepare for migration from version model \"\(migrationManager.sourceModel)\" to version model \"\(migrationManager.destinationModel)\".") + + GCDQueue.Main.async { + + completion(PersistentStoreResult(.MigrationFailed)) + } + return + } + + } + /** Asynchronously adds to the stack an SQLite store from the given SQLite file name. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.) @@ -123,7 +253,7 @@ public extension DataStack { :param: fileURL the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a ".sqlite" file in the "Application Support" directory. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them. :param: configuration an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them. - :param: completion the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `PersistentStoreResult` argument indicates the result. + :param: completion the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `PersistentStoreResult` argument indicates the result. */ public func addSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, completion: (PersistentStoreResult) -> Void) { diff --git a/CoreStore/NSError+CoreStore.swift b/CoreStore/NSError+CoreStore.swift index cfa5fb2..20f6049 100644 --- a/CoreStore/NSError+CoreStore.swift +++ b/CoreStore/NSError+CoreStore.swift @@ -44,6 +44,21 @@ public enum CoreStoreErrorCode: Int { The `NSPersistentStore` could note be initialized because another store existed at the specified `NSURL`. */ case DifferentPersistentStoreExistsAtURL + + /** + The `NSPersistentStore` specified could not be found. + */ + case PersistentStoreNotFound + + /** + An `NSMappingModel` could not be found for a specific source and destination model versions. + */ + case MappingModelNotFound + + /** + An `NSMigrationManager` prepared to migrate the store. + */ + case MigrationFailed }