From 9676e3aca299312ac51d44a4cd118fca3378521e Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 26 Jul 2015 10:07:42 +0900 Subject: [PATCH] addSQLiteStoreAndWait() does not support auto-migrating stores anymore; use the asynchronous addSQLiteStore(..., completion:) method instead. --- CoreStore/Migrating/DataStack+Migration.swift | 24 ++++++--------- CoreStore/Setting Up/CoreStore+Setup.swift | 16 ++++------ CoreStore/Setting Up/DataStack.swift | 30 +++++-------------- ...etchingAndQueryingDemoViewController.swift | 2 +- .../ListObserverDemoViewController.swift | 2 +- .../StackSetupDemoViewController.swift | 8 ++--- .../TransactionsDemoViewController.swift | 2 +- CoreStoreTests/CoreStoreTests.swift | 4 +-- README.md | 20 ++++++++++--- 9 files changed, 48 insertions(+), 60 deletions(-) diff --git a/CoreStore/Migrating/DataStack+Migration.swift b/CoreStore/Migrating/DataStack+Migration.swift index 1da675b..f1720f3 100644 --- a/CoreStore/Migrating/DataStack+Migration.swift +++ b/CoreStore/Migrating/DataStack+Migration.swift @@ -79,11 +79,11 @@ public extension DataStack { - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them. - parameter 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 `fileName` explicitly for each of them. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - - parameter resetStoreOnMigrationFailure: Set to true to delete the store on migration failure; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. + - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - parameter 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. This closure is NOT executed if an error is thrown, but will be executed with a `.Failure` result if an error occurs asynchronously. - returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required */ - public func addSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, resetStoreOnMigrationFailure: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { + public func addSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { return try self.addSQLiteStore( fileURL: applicationSupportDirectory.URLByAppendingPathComponent( @@ -92,7 +92,7 @@ public extension DataStack { ), configuration: configuration, mappingModelBundles: mappingModelBundles, - resetStoreOnMigrationFailure: resetStoreOnMigrationFailure, + resetStoreOnModelMismatch: resetStoreOnModelMismatch, completion: completion ) } @@ -103,11 +103,11 @@ public extension DataStack { - parameter 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. - parameter 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. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - - parameter resetStoreOnMigrationFailure: Set to true to delete the store on migration failure; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. + - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - parameter 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. This closure is NOT executed if an error is thrown, but will be executed with a `.Failure` result if an error occurs asynchronously. - returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required */ - public func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), resetStoreOnMigrationFailure: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { + public func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { CoreStore.assert( fileURL.fileURL, @@ -117,10 +117,7 @@ public extension DataStack { let coordinator = self.coordinator; if let store = coordinator.persistentStoreForURL(fileURL) { - let isExistingStoreAutomigrating = store.options?[NSMigratePersistentStoresAutomaticallyOption] as? Bool == true - if store.type == NSSQLiteStoreType - && isExistingStoreAutomigrating && store.configurationName == (configuration ?? Into.defaultConfigurationName) { GCDQueue.Main.async { @@ -165,7 +162,7 @@ public extension DataStack { if case .Failure(let error) = result { - if resetStoreOnMigrationFailure && error.isCoreDataMigrationError { + if resetStoreOnModelMismatch && error.isCoreDataMigrationError { fileManager.removeSQLiteStoreAtURL(fileURL) do { @@ -173,8 +170,7 @@ public extension DataStack { let store = try self.addSQLiteStoreAndWait( fileURL: fileURL, configuration: configuration, - automigrating: false, - resetStoreOnMigrationFailure: false + resetStoreOnModelMismatch: false ) GCDQueue.Main.async { @@ -198,8 +194,7 @@ public extension DataStack { let store = try self.addSQLiteStoreAndWait( fileURL: fileURL, configuration: configuration, - automigrating: false, - resetStoreOnMigrationFailure: false + resetStoreOnModelMismatch: false ) completion(PersistentStoreResult(store)) @@ -217,8 +212,7 @@ public extension DataStack { let store = try self.addSQLiteStoreAndWait( fileURL: fileURL, configuration: configuration, - automigrating: false, - resetStoreOnMigrationFailure: false + resetStoreOnModelMismatch: false ) GCDQueue.Main.async { diff --git a/CoreStore/Setting Up/CoreStore+Setup.swift b/CoreStore/Setting Up/CoreStore+Setup.swift index 499c715..bd81e0b 100644 --- a/CoreStore/Setting Up/CoreStore+Setup.swift +++ b/CoreStore/Setting Up/CoreStore+Setup.swift @@ -48,17 +48,15 @@ public extension CoreStore { - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil. - - parameter automigrating: Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true. - - parameter resetStoreOnMigrationFailure: Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false + - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false - returns: the `NSPersistentStore` added to the stack. */ - public static func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) throws -> NSPersistentStore { + public static func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { return try self.defaultStack.addSQLiteStoreAndWait( fileName: fileName, configuration: configuration, - automigrating: automigrating, - resetStoreOnMigrationFailure: resetStoreOnMigrationFailure + resetStoreOnModelMismatch: resetStoreOnModelMismatch ) } @@ -67,17 +65,15 @@ public extension CoreStore { - parameter 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. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil. - - parameter automigrating: Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true. - - parameter resetStoreOnMigrationFailure: Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. + - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - returns: the `NSPersistentStore` added to the stack. */ - public static func addSQLiteStoreAndWait(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) throws -> NSPersistentStore { + public static func addSQLiteStoreAndWait(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { return try self.defaultStack.addSQLiteStoreAndWait( fileURL: fileURL, configuration: configuration, - automigrating: automigrating, - resetStoreOnMigrationFailure: resetStoreOnMigrationFailure + resetStoreOnModelMismatch: resetStoreOnModelMismatch ) } } diff --git a/CoreStore/Setting Up/DataStack.swift b/CoreStore/Setting Up/DataStack.swift index 2cde3ab..31e1574 100644 --- a/CoreStore/Setting Up/DataStack.swift +++ b/CoreStore/Setting Up/DataStack.swift @@ -129,11 +129,10 @@ public final class DataStack { - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them. - parameter 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 `fileName` explicitly for each of them. - - parameter automigrating: Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true. - - parameter resetStoreOnMigrationFailure: Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false + - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false - returns: the `NSPersistentStore` added to the stack. */ - public func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) throws -> NSPersistentStore { + public func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { return try self.addSQLiteStoreAndWait( fileURL: applicationSupportDirectory.URLByAppendingPathComponent( @@ -141,8 +140,7 @@ public final class DataStack { isDirectory: false ), configuration: configuration, - automigrating: automigrating, - resetStoreOnMigrationFailure: resetStoreOnMigrationFailure + resetStoreOnModelMismatch: resetStoreOnModelMismatch ) } @@ -151,11 +149,10 @@ public final class DataStack { - parameter 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. - parameter 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. - - parameter automigrating: Set to true to configure Core Data auto-migration, or false to disable. If not specified, defaults to true. - - parameter resetStoreOnMigrationFailure: Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. + - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - returns: the `NSPersistentStore` added to the stack. */ - public func addSQLiteStoreAndWait(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) throws -> NSPersistentStore { + public func addSQLiteStoreAndWait(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { CoreStore.assert( fileURL.fileURL, @@ -165,10 +162,7 @@ public final class DataStack { let coordinator = self.coordinator; if let store = coordinator.persistentStoreForURL(fileURL) { - let isExistingStoreAutomigrating = (store.options?[NSMigratePersistentStoresAutomaticallyOption] as? Bool) == true - if store.type == NSSQLiteStoreType - && isExistingStoreAutomigrating == automigrating && store.configurationName == (configuration ?? Into.defaultConfigurationName) { return store @@ -204,11 +198,7 @@ public final class DataStack { NSSQLiteStoreType, configuration: configuration, URL: fileURL, - options: [ - NSSQLitePragmasOption: ["journal_mode": "WAL"], - NSInferMappingModelAutomaticallyOption: true, - NSMigratePersistentStoresAutomaticallyOption: automigrating - ] + options: [NSSQLitePragmasOption: ["journal_mode": "WAL"]] ) } catch { @@ -224,7 +214,7 @@ public final class DataStack { } if let error = storeError - where (resetStoreOnMigrationFailure && error.isCoreDataMigrationError) { + where (resetStoreOnModelMismatch && error.isCoreDataMigrationError) { fileManager.removeSQLiteStoreAtURL(fileURL) @@ -237,11 +227,7 @@ public final class DataStack { NSSQLiteStoreType, configuration: configuration, URL: fileURL, - options: [ - NSSQLitePragmasOption: ["journal_mode": "WAL"], - NSInferMappingModelAutomaticallyOption: true, - NSMigratePersistentStoresAutomaticallyOption: automigrating - ] + options: [NSSQLitePragmasOption: ["journal_mode": "WAL"]] ) } catch { diff --git a/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift index 2c7b6e4..819e789 100644 --- a/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Fetching and Querying Demo/FetchingAndQueryingDemoViewController.swift @@ -18,7 +18,7 @@ private struct Static { try! dataStack.addSQLiteStoreAndWait( fileName: "TimeZoneDemo.sqlite", configuration: "FetchingAndQueryingDemo", - resetStoreOnMigrationFailure: true + resetStoreOnModelMismatch: true ) dataStack.beginSynchronous { (transaction) -> Void in diff --git a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift index 8c1bc35..58c754d 100644 --- a/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/ListObserverDemoViewController.swift @@ -17,7 +17,7 @@ private struct Static { try! CoreStore.addSQLiteStoreAndWait( fileName: "ColorsDemo.sqlite", configuration: "ObservingDemo", - resetStoreOnMigrationFailure: true + resetStoreOnModelMismatch: true ) return CoreStore.monitorSectionedList( diff --git a/CoreStoreDemo/CoreStoreDemo/Stack Setup Demo/StackSetupDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/Stack Setup Demo/StackSetupDemoViewController.swift index 6f903c8..305f0a3 100644 --- a/CoreStoreDemo/CoreStoreDemo/Stack Setup Demo/StackSetupDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Stack Setup Demo/StackSetupDemoViewController.swift @@ -21,12 +21,12 @@ private struct Static { try! dataStack.addSQLiteStoreAndWait( fileName: "AccountsDemo_FB_Male.sqlite", configuration: maleConfiguration, - resetStoreOnMigrationFailure: true + resetStoreOnModelMismatch: true ) try! dataStack.addSQLiteStoreAndWait( fileName: "AccountsDemo_FB_Female.sqlite", configuration: femaleConfiguration, - resetStoreOnMigrationFailure: true + resetStoreOnModelMismatch: true ) dataStack.beginSynchronous { (transaction) -> Void in @@ -55,12 +55,12 @@ private struct Static { try! dataStack.addSQLiteStoreAndWait( fileName: "AccountsDemo_TW_Male.sqlite", configuration: maleConfiguration, - resetStoreOnMigrationFailure: true + resetStoreOnModelMismatch: true ) try! dataStack.addSQLiteStoreAndWait( fileName: "AccountsDemo_TW_Female.sqlite", configuration: femaleConfiguration, - resetStoreOnMigrationFailure: true + resetStoreOnModelMismatch: true ) dataStack.beginSynchronous { (transaction) -> Void in diff --git a/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift index 95aa443..e714472 100644 --- a/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Transactions Demo/TransactionsDemoViewController.swift @@ -21,7 +21,7 @@ private struct Static { try! CoreStore.addSQLiteStoreAndWait( fileName: "PlaceDemo.sqlite", configuration: "TransactionsDemo", - resetStoreOnMigrationFailure: true + resetStoreOnModelMismatch: true ) var place = CoreStore.fetchOne(From(Place)) diff --git a/CoreStoreTests/CoreStoreTests.swift b/CoreStoreTests/CoreStoreTests.swift index 3b9623c..423b7d3 100644 --- a/CoreStoreTests/CoreStoreTests.swift +++ b/CoreStoreTests/CoreStoreTests.swift @@ -87,7 +87,7 @@ class CoreStoreTests: XCTestCase { do { - try stack.addSQLiteStoreAndWait(fileName: "ConfigStore1.sqlite", configuration: "Config1", resetStoreOnMigrationFailure: true) + try stack.addSQLiteStoreAndWait(fileName: "ConfigStore1.sqlite", configuration: "Config1", resetStoreOnModelMismatch: true) } catch let error as NSError { @@ -96,7 +96,7 @@ class CoreStoreTests: XCTestCase { do { - try stack.addSQLiteStoreAndWait(fileName: "ConfigStore2.sqlite", configuration: "Config2", resetStoreOnMigrationFailure: true) + try stack.addSQLiteStoreAndWait(fileName: "ConfigStore2.sqlite", configuration: "Config2", resetStoreOnModelMismatch: true) } catch let error as NSError { diff --git a/README.md b/README.md index 7f886bc..22f8d2f 100644 --- a/README.md +++ b/README.md @@ -169,7 +169,7 @@ catch { This one-liner does the following: - Triggers the lazy-initialization of `CoreStore.defaultStack` with a default `DataStack` - Sets up the stack's `NSPersistentStoreCoordinator`, the root saving `NSManagedObjectContext`, and the read-only main `NSManagedObjectContext` -- Adds an automigrating SQLite store in the *"Application Support"* directory with the file name *"[App bundle name].sqlite"* +- Adds an SQLite store in the *"Application Support"* directory with the file name *"[App bundle name].sqlite"* - Creates and returns the `NSPersistentStore` instance on success, or an `NSError` on failure For most cases, this configuration is usable as it is. But for more hardcore settings, refer to this extensive example: @@ -192,7 +192,7 @@ do { try dataStack.addSQLiteStore( fileURL: sqliteFileURL, // set the target file URL for the sqlite file configuration: "Config2", // use entities from the "Config2" configuration in the .xcdatamodeld file - resetStoreOnMigrationFailure: true, + resetStoreOnModelMismatch: true, completion: { (result) -> Void in switch result { case .Success(let persistentStore): @@ -271,8 +271,20 @@ catch { print("Failed adding sqlite store with error: \(error as NSError)" } ``` -The `completion` block is called -PersistentStoreResult +The `completion` block reports a `PersistentStoreResult` that indicates success or failure. + +`addSQLiteStore(...)` throws an error if the store at the specified URL conflicts with an existing store in the `DataStack`, or if an existing sqlite file could not be read. If an error is thrown, the `completion` block will not be executed. + +Notice that this method also returns an optional `NSProgress`. If `nil`, no migrations are needed, thus progress reporting is unnecessary as well. If not `nil`, you can use this to track migration progress by using standard KVO on the "fractionCompleted" key, or by using a closure-based utility exposed in *NSProgress+Convenience.swift*: +```swift +progress?.setProgressHandler { [weak self] (progress) -> Void in + self?.progressView?.setProgress(Float(progress.fractionCompleted), animated: true) + self?.percentLabel?.text = progress.localizedDescription // "50% completed" + self?.stepLabel?.text = progress.localizedAdditionalDescription // "0 of 2" +} +``` +This closure is executed on the main thread so UIKit calls can be done safely. + ### Incremental migrations (README pending)