From 7bddcaa4a2dc3f437090f801d651fae486d3cb44 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Wed, 24 Feb 2016 16:54:39 +0900 Subject: [PATCH 001/108] swift 2.2 (Xcode 7.3 beta 4) updates --- .../Importing Data/ImportableObject.swift | 2 +- .../ImportableUniqueObject.swift | 4 ++-- .../Internal/NSManagedObjectModel+Setup.swift | 20 ++++++++++--------- CoreStore/Logging/CoreStore+Logging.swift | 6 +++--- CoreStore/Migrating/MigrationChain.swift | 14 +++++++------ CoreStore/Observing/ListObserver.swift | 2 +- CoreStore/Observing/ObjectObserver.swift | 2 +- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/CoreStore/Importing Data/ImportableObject.swift b/CoreStore/Importing Data/ImportableObject.swift index a24b767..7570f32 100644 --- a/CoreStore/Importing Data/ImportableObject.swift +++ b/CoreStore/Importing Data/ImportableObject.swift @@ -53,7 +53,7 @@ public protocol ImportableObject: class { /** The data type for the import source. This is most commonly an `NSDictionary` or another external source such as an `NSUserDefaults`. */ - typealias ImportSource + associatedtype ImportSource /** Return `true` if an object should be created from `source`. Return `false` to ignore and skip `source`. The default implementation returns `true`. diff --git a/CoreStore/Importing Data/ImportableUniqueObject.swift b/CoreStore/Importing Data/ImportableUniqueObject.swift index 5c23467..d5f7dc4 100644 --- a/CoreStore/Importing Data/ImportableUniqueObject.swift +++ b/CoreStore/Importing Data/ImportableUniqueObject.swift @@ -54,12 +54,12 @@ public protocol ImportableUniqueObject: ImportableObject { /** The data type for the import source. This is most commonly an `NSDictionary` or another external source such as an `NSUserDefaults`. */ - typealias ImportSource + associatedtype ImportSource /** The data type for the entity's unique ID attribute */ - typealias UniqueIDType: NSObject + associatedtype UniqueIDType: NSObject /** The keyPath to the entity's unique ID attribute diff --git a/CoreStore/Internal/NSManagedObjectModel+Setup.swift b/CoreStore/Internal/NSManagedObjectModel+Setup.swift index 9d0bac0..ef22705 100644 --- a/CoreStore/Internal/NSManagedObjectModel+Setup.swift +++ b/CoreStore/Internal/NSManagedObjectModel+Setup.swift @@ -155,11 +155,12 @@ internal extension NSManagedObjectModel { @nonobjc internal func entityTypesMapping() -> [String: NSManagedObject.Type] { - return self.entityNameMapping.reduce([:]) { (var mapping, pair) in + var mapping = [String: NSManagedObject.Type]() + self.entityNameMapping.forEach { (className, entityName) in - mapping[pair.1] = (NSClassFromString(pair.0)! as! NSManagedObject.Type) - return mapping + mapping[entityName] = (NSClassFromString(className)! as! NSManagedObject.Type) } + return mapping } @nonobjc internal func mergedModels() -> [NSManagedObjectModel] { @@ -249,15 +250,16 @@ internal extension NSManagedObjectModel { return mapping as! [String: String] } - let mapping = self.entities.reduce([String: String]()) { - (var mapping, entityDescription) -> [String: String] in + var mapping = [String: String]() + self.entities.forEach { - if let entityName = entityDescription.name { + guard let entityName = $0.name else { - let className = entityDescription.managedObjectClassName - mapping[className] = entityName + return } - return mapping + + let className = $0.managedObjectClassName + mapping[className] = entityName } setAssociatedCopiedObject( mapping as NSDictionary, diff --git a/CoreStore/Logging/CoreStore+Logging.swift b/CoreStore/Logging/CoreStore+Logging.swift index 5bfe79c..76a3bc7 100644 --- a/CoreStore/Logging/CoreStore+Logging.swift +++ b/CoreStore/Logging/CoreStore+Logging.swift @@ -38,7 +38,7 @@ public extension CoreStore { // MARK: Internal - internal static func log(level: LogLevel, message: String, fileName: StaticString = __FILE__, lineNumber: Int = __LINE__, functionName: StaticString = __FUNCTION__) { + internal static func log(level: LogLevel, message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) { self.logger.log( level: level, @@ -49,7 +49,7 @@ public extension CoreStore { ) } - internal static func handleError(error: NSError, _ message: String, fileName: StaticString = __FILE__, lineNumber: Int = __LINE__, functionName: StaticString = __FUNCTION__) { + internal static func handleError(error: NSError, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) { self.logger.handleError( error: error, @@ -60,7 +60,7 @@ public extension CoreStore { ) } - internal static func assert(@autoclosure condition: () -> Bool, _ message: String, fileName: StaticString = __FILE__, lineNumber: Int = __LINE__, functionName: StaticString = __FUNCTION__) { + internal static func assert(@autoclosure condition: () -> Bool, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) { self.logger.assert( condition, diff --git a/CoreStore/Migrating/MigrationChain.swift b/CoreStore/Migrating/MigrationChain.swift index b57cfd6..eb1a3a0 100644 --- a/CoreStore/Migrating/MigrationChain.swift +++ b/CoreStore/Migrating/MigrationChain.swift @@ -111,15 +111,17 @@ public struct MigrationChain: NilLiteralConvertible, StringLiteralConvertible, D public init(dictionaryLiteral elements: (String, String)...) { var valid = true - let versionTree = elements.reduce([String: String]()) { (var versionTree, tuple: (String, String)) -> [String: String] in + var versionTree = [String: String]() + elements.forEach { (sourceVersion, destinationVersion) in - if let _ = versionTree.updateValue(tuple.1, forKey: tuple.0) { + guard let _ = versionTree.updateValue(destinationVersion, forKey: sourceVersion) else { - CoreStore.assert(false, "\(typeName(MigrationChain))'s migration chain could not be created due to ambiguous version paths.") - - valid = false + return } - return versionTree + + CoreStore.assert(false, "\(typeName(MigrationChain))'s migration chain could not be created due to ambiguous version paths.") + + valid = false } let leafVersions = Set( elements.filter { (tuple: (String, String)) -> Bool in diff --git a/CoreStore/Observing/ListObserver.swift b/CoreStore/Observing/ListObserver.swift index 67ced6a..61c11c8 100644 --- a/CoreStore/Observing/ListObserver.swift +++ b/CoreStore/Observing/ListObserver.swift @@ -45,7 +45,7 @@ public protocol ListObserver: class { /** The `NSManagedObject` type for the observed list */ - typealias ListEntityType: NSManagedObject + associatedtype ListEntityType: NSManagedObject /** Handles processing just before a change to the observed list occurs diff --git a/CoreStore/Observing/ObjectObserver.swift b/CoreStore/Observing/ObjectObserver.swift index 0188b0a..ca662bd 100644 --- a/CoreStore/Observing/ObjectObserver.swift +++ b/CoreStore/Observing/ObjectObserver.swift @@ -42,7 +42,7 @@ public protocol ObjectObserver: class { /** The `NSManagedObject` type for the observed object */ - typealias ObjectEntityType: NSManagedObject + associatedtype ObjectEntityType: NSManagedObject /** Handles processing just before a change to the observed `object` occurs From 8f09f902944695df36bef03ea99cd8d5984e3237 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Wed, 24 Feb 2016 18:57:03 +0900 Subject: [PATCH 002/108] Changed default directories to comply with Apple's guidelines (TODO: update README) --- CoreStore/Migrating/CoreStore+Migration.swift | 16 ++++----- CoreStore/Migrating/DataStack+Migration.swift | 22 ++++++------ CoreStore/Setting Up/CoreStore+Setup.swift | 6 ++-- CoreStore/Setting Up/DataStack.swift | 34 ++++++++++++------- README.md | 3 +- 5 files changed, 46 insertions(+), 35 deletions(-) diff --git a/CoreStore/Migrating/CoreStore+Migration.swift b/CoreStore/Migrating/CoreStore+Migration.swift index f42a03f..df4d24b 100644 --- a/CoreStore/Migrating/CoreStore+Migration.swift +++ b/CoreStore/Migrating/CoreStore+Migration.swift @@ -37,7 +37,7 @@ public extension CoreStore { /** Asynchronously adds to the `defaultStack` 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`.) - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). 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 fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). 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 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. @@ -58,14 +58,14 @@ public extension CoreStore { /** Asynchronously adds to the `defaultStack` an SQLite store from the given SQLite file URL. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.) - - 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 (or the "Caches" directory on tvOS). Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them. + - 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 (or the "Caches/" directory on tvOS). 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 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 static func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { + public static func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { return try self.defaultStack.addSQLiteStore( fileURL: fileURL, @@ -79,7 +79,7 @@ public extension CoreStore { /** Using the `defaultStack`, migrates an SQLite store with the specified filename to the `DataStack`'s managed object model version WITHOUT adding the migrated store to the data stack. - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). + - parameter fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. @@ -98,13 +98,13 @@ public extension CoreStore { /** Using the `defaultStack`, migrates an SQLite store at the specified file URL and configuration name to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack. - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). + - parameter fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required */ - public static func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? { + public static func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? { return try self.defaultStack.upgradeSQLiteStoreIfNeeded( fileURL: fileURL, @@ -117,7 +117,7 @@ public extension CoreStore { /** Using the `defaultStack`, checks for the required migrations needed for the store with the specified filename and configuration to be migrated to the `DataStack`'s managed object model version. This method throws an error if the store does not exist, if inspection of the store failed, or no mapping model was found/inferred. - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). + - parameter fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. :return: an array of `MigrationType`s indicating the chain of migrations required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed. @@ -141,7 +141,7 @@ public extension CoreStore { :return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed. */ @warn_unused_result - public static func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] { + public static func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] { return try self.defaultStack.requiredMigrationsForSQLiteStore( fileURL: fileURL, diff --git a/CoreStore/Migrating/DataStack+Migration.swift b/CoreStore/Migrating/DataStack+Migration.swift index 8017d0d..b3e77bc 100644 --- a/CoreStore/Migrating/DataStack+Migration.swift +++ b/CoreStore/Migrating/DataStack+Migration.swift @@ -78,7 +78,7 @@ public extension DataStack { /** 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`.) - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). 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 fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). 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 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. @@ -88,7 +88,7 @@ public extension DataStack { 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: defaultDirectory.URLByAppendingPathComponent( + fileURL: defaultRootDirectory.URLByAppendingPathComponent( fileName, isDirectory: false ), @@ -102,14 +102,14 @@ public extension DataStack { /** Asynchronously adds to the stack an SQLite store from the given SQLite file URL. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.) - - 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 (or the "Caches" directory on tvOS). Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them. + - 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 (or the "Caches/" directory on tvOS). 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 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(), resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { + public func addSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { CoreStore.assert( fileURL.fileURL, @@ -233,7 +233,7 @@ public extension DataStack { /** Migrates an SQLite store with the specified filename to the `DataStack`'s managed object model version WITHOUT adding the migrated store to the data stack. - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). + - parameter fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. @@ -242,7 +242,7 @@ public extension DataStack { public func upgradeSQLiteStoreIfNeeded(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? { return try self.upgradeSQLiteStoreIfNeeded( - fileURL: defaultDirectory.URLByAppendingPathComponent( + fileURL: defaultRootDirectory.URLByAppendingPathComponent( fileName, isDirectory: false ), @@ -255,13 +255,13 @@ public extension DataStack { /** Migrates an SQLite store at the specified file URL and configuration name to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack. - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). + - parameter fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required */ - public func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? { + public func upgradeSQLiteStoreIfNeeded(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? { let metadata: [String: AnyObject] do { @@ -293,7 +293,7 @@ public extension DataStack { /** Checks for the required migrations needed for the store with the specified filename and configuration to be migrated to the `DataStack`'s managed object model version. This method throws an error if the store does not exist, if inspection of the store failed, or no mapping model was found/inferred. - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). + - parameter fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. :return: an array of `MigrationType`s indicating the chain of migrations required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed. @@ -302,7 +302,7 @@ public extension DataStack { public func requiredMigrationsForSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] { return try requiredMigrationsForSQLiteStore( - fileURL: defaultDirectory.URLByAppendingPathComponent( + fileURL: defaultRootDirectory.URLByAppendingPathComponent( fileName, isDirectory: false ), @@ -320,7 +320,7 @@ public extension DataStack { :return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed. */ @warn_unused_result - public func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] { + public func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] { let metadata: [String : AnyObject] do { diff --git a/CoreStore/Setting Up/CoreStore+Setup.swift b/CoreStore/Setting Up/CoreStore+Setup.swift index 2ebdd44..97cde84 100644 --- a/CoreStore/Setting Up/CoreStore+Setup.swift +++ b/CoreStore/Setting Up/CoreStore+Setup.swift @@ -72,7 +72,7 @@ public extension CoreStore { /** Adds to the `defaultStack` an SQLite store from the given SQLite file name. - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). A new SQLite file will be created if it does not exist. + - parameter fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). 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 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. @@ -89,12 +89,12 @@ public extension CoreStore { /** Adds to the `defaultStack` an SQLite store from the given SQLite file URL. - - 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 (or the "Caches" directory on tvOS). + - 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 (or the "Caches/" directory on tvOS). - parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil. - 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, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { + public static func addSQLiteStoreAndWait(fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { return try self.defaultStack.addSQLiteStoreAndWait( fileURL: fileURL, diff --git a/CoreStore/Setting Up/DataStack.swift b/CoreStore/Setting Up/DataStack.swift index 76ddacd..ad02822 100644 --- a/CoreStore/Setting Up/DataStack.swift +++ b/CoreStore/Setting Up/DataStack.swift @@ -30,17 +30,26 @@ import CoreData #endif +// TODO: move these to PersistentStore wrapper + #if os(tvOS) - internal let deviceDirectorySearchPath = NSSearchPathDirectory.CachesDirectory + internal let systemDirectorySearchPath = NSSearchPathDirectory.CachesDirectory #else - internal let deviceDirectorySearchPath = NSSearchPathDirectory.ApplicationSupportDirectory + internal let systemDirectorySearchPath = NSSearchPathDirectory.ApplicationSupportDirectory #endif -internal let defaultDirectory = NSFileManager.defaultManager().URLsForDirectory(deviceDirectorySearchPath, inDomains: .UserDomainMask).first! - +internal let defaultSystemDirectory = NSFileManager.defaultManager().URLsForDirectory( + systemDirectorySearchPath, + inDomains: .UserDomainMask +).first! +internal let defaultRootDirectory = defaultSystemDirectory.URLByAppendingPathComponent( + NSBundle.mainBundle().bundleIdentifier ?? "com.CoreStore.DataStack", + isDirectory: true +) internal let applicationName = (NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData" - -internal let defaultSQLiteStoreURL = defaultDirectory.URLByAppendingPathComponent(applicationName, isDirectory: false).URLByAppendingPathExtension("sqlite") +internal let defaultSQLiteStoreFileURL = defaultRootDirectory + .URLByAppendingPathComponent(applicationName, isDirectory: false) + .URLByAppendingPathExtension("sqlite") // MARK: - DataStack @@ -160,7 +169,7 @@ public final class DataStack { /** Adds to the stack an SQLite store from the given SQLite file name. - - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). 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 fileName: the local filename for the SQLite persistent store in the "Application Support/" directory (or the "Caches/" directory on tvOS). 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 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. @@ -168,9 +177,10 @@ public final class DataStack { public func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { return try self.addSQLiteStoreAndWait( - fileURL: defaultDirectory.URLByAppendingPathComponent( - fileName, - isDirectory: false + fileURL: defaultRootDirectory + .URLByAppendingPathComponent( + fileName, + isDirectory: false ), configuration: configuration, resetStoreOnModelMismatch: resetStoreOnModelMismatch @@ -180,12 +190,12 @@ public final class DataStack { /** Adds to the stack an SQLite store from the given SQLite file URL. - - 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 (or the "Caches" directory on tvOS). Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them. + - 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 (or the "Caches/" directory on tvOS). 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 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, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { + public func addSQLiteStoreAndWait(fileURL fileURL: NSURL = defaultSQLiteStoreFileURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { CoreStore.assert( fileURL.fileURL, diff --git a/README.md b/README.md index 81925b6..b23449e 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 SQLite store in the *"Application Support"* directory (or the *"Caches"* directory on tvOS) with the file name *"[App bundle name].sqlite"* +- Adds an SQLite store in the *"Application Support/"* directory (or the *"Caches/"* directory on tvOS) 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: @@ -1214,6 +1214,7 @@ The protocols above had their methods renamed as well, to retain the natural lan - New migration utilities! (README still pending) Check out *DataStack+Migration.swift* and *CoreStore+Migration.swift* for the new methods, as well as *DataStack.swift* for its new initializer. + # Contributions While CoreStore's design is pretty solid and the unit test and demo app work well, CoreStore is pretty much still in its early stage. With more exposure to production code usage and criticisms from the developer community, CoreStore hopes to mature as well. Please feel free to report any issues, suggestions, or criticisms! From f19a0d29eb0cecdde62c920be6da515e8f6e4168 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 24 Feb 2016 21:52:35 +0900 Subject: [PATCH 003/108] added obsolete annotations to previously deprecated methods --- CoreStore.podspec | 2 +- CoreStore/Info.plist | 2 +- .../AsynchronousDataTransaction.swift | 2 +- .../CoreStore+Transaction.swift | 2 +- .../DataStack+Transaction.swift | 2 +- .../SynchronousDataTransaction.swift | 2 +- .../UnsafeDataTransaction.swift | 4 +- CoreStore/Setting Up/DataStack.swift | 125 +++++++----------- 8 files changed, 54 insertions(+), 87 deletions(-) diff --git a/CoreStore.podspec b/CoreStore.podspec index 989b6f6..1201e31 100644 --- a/CoreStore.podspec +++ b/CoreStore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CoreStore" - s.version = "1.5.1" + s.version = "2.0.0" s.license = "MIT" s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift" s.homepage = "https://github.com/JohnEstropia/CoreStore" diff --git a/CoreStore/Info.plist b/CoreStore/Info.plist index d18bd0a..7e7479f 100644 --- a/CoreStore/Info.plist +++ b/CoreStore/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.5.1 + 2.0.0 CFBundleSignature ???? CFBundleVersion diff --git a/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift b/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift index 8a2e293..8863cff 100644 --- a/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift +++ b/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift @@ -192,7 +192,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { /** Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. This method should not be used after the `commit()` method was already called once. */ - @available(*, deprecated=1.3.4, message="Resetting the context is inherently unsafe. This method will be removed in the near future. Use `beginUnsafe()` to create transactions with `undo` support.") + @available(*, deprecated=1.3.4, obsoleted=2.0.0, message="Resetting the context is inherently unsafe. This method will be removed in the near future. Use `beginUnsafe()` to create transactions with `undo` support.") public func rollback() { CoreStore.assert( diff --git a/CoreStore/Saving and Processing/CoreStore+Transaction.swift b/CoreStore/Saving and Processing/CoreStore+Transaction.swift index 2023d46..873a264 100644 --- a/CoreStore/Saving and Processing/CoreStore+Transaction.swift +++ b/CoreStore/Saving and Processing/CoreStore+Transaction.swift @@ -71,7 +71,7 @@ public extension CoreStore { self.defaultStack.refreshAllObjectsAsFaults() } - @available(*, deprecated=1.3.1, renamed="beginUnsafe") + @available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="beginUnsafe") @warn_unused_result public static func beginDetached() -> UnsafeDataTransaction { diff --git a/CoreStore/Saving and Processing/DataStack+Transaction.swift b/CoreStore/Saving and Processing/DataStack+Transaction.swift index 9277d8b..7f565be 100644 --- a/CoreStore/Saving and Processing/DataStack+Transaction.swift +++ b/CoreStore/Saving and Processing/DataStack+Transaction.swift @@ -93,7 +93,7 @@ public extension DataStack { self.mainContext.refreshAllObjectsAsFaults() } - @available(*, deprecated=1.3.1, renamed="beginUnsafe") + @available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="beginUnsafe") @warn_unused_result public func beginDetached() -> UnsafeDataTransaction { diff --git a/CoreStore/Saving and Processing/SynchronousDataTransaction.swift b/CoreStore/Saving and Processing/SynchronousDataTransaction.swift index cb11bf3..bb6404e 100644 --- a/CoreStore/Saving and Processing/SynchronousDataTransaction.swift +++ b/CoreStore/Saving and Processing/SynchronousDataTransaction.swift @@ -182,7 +182,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction { /** Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. This method should not be used after the `commit()` method was already called once. */ - @available(*, deprecated=1.3.4, message="Resetting the context is inherently unsafe. This method will be removed in the near future. Use `beginUnsafe()` to create transactions with `undo` support.") + @available(*, deprecated=1.3.4, obsoleted=2.0.0, message="Resetting the context is inherently unsafe. This method will be removed in the near future. Use `beginUnsafe()` to create transactions with `undo` support.") public func rollback() { CoreStore.assert( diff --git a/CoreStore/Saving and Processing/UnsafeDataTransaction.swift b/CoreStore/Saving and Processing/UnsafeDataTransaction.swift index 74aaafe..26224c7 100644 --- a/CoreStore/Saving and Processing/UnsafeDataTransaction.swift +++ b/CoreStore/Saving and Processing/UnsafeDataTransaction.swift @@ -30,7 +30,7 @@ import CoreData #endif -@available(*, deprecated=1.3.1, renamed="UnsafeDataTransaction") +@available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="UnsafeDataTransaction") public typealias DetachedDataTransaction = UnsafeDataTransaction @@ -119,7 +119,7 @@ public final class UnsafeDataTransaction: BaseDataTransaction { return self.context } - @available(*, deprecated=1.3.1, renamed="beginUnsafe") + @available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="beginUnsafe") @warn_unused_result public func beginDetached() -> UnsafeDataTransaction { diff --git a/CoreStore/Setting Up/DataStack.swift b/CoreStore/Setting Up/DataStack.swift index ad02822..b68f643 100644 --- a/CoreStore/Setting Up/DataStack.swift +++ b/CoreStore/Setting Up/DataStack.swift @@ -129,41 +129,28 @@ public final class DataStack { - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`. - returns: the `NSPersistentStore` added to the stack. */ + @available(*, deprecated=2.0.0, renamed="beginUnsafe") public func addInMemoryStoreAndWait(configuration configuration: String? = nil) throws -> NSPersistentStore { - let coordinator = self.coordinator; - - var store: NSPersistentStore? - var storeError: NSError? - coordinator.performBlockAndWait { - - do { - - store = try coordinator.addPersistentStoreWithType( - NSInMemoryStoreType, - configuration: configuration, - URL: nil, - options: nil - ) - } - catch { - - storeError = error as NSError - } - } - - if let store = store { + do { + let store = try self.coordinator.addPersistentStoreSynchronously( + NSInMemoryStoreType, + configuration: configuration, + URL: nil, + options: nil + ) self.updateMetadataForPersistentStore(store) return store } - - let error = storeError ?? NSError(coreStoreErrorCode: .UnknownError) - CoreStore.handleError( - error, - "Failed to add in-memory \(typeName(NSPersistentStore)) to the stack." - ) - throw error + catch { + + CoreStore.handleError( + error as NSError, + "Failed to add in-memory \(typeName(NSPersistentStore)) to the stack." + ) + throw error + } } /** @@ -227,68 +214,50 @@ public final class DataStack { attributes: nil ) - var store: NSPersistentStore? - var storeError: NSError? let options = self.optionsForSQLiteStore() - coordinator.performBlockAndWait { + do { + + let store = try coordinator.addPersistentStoreSynchronously( + NSSQLiteStoreType, + configuration: configuration, + URL: fileURL, + options: options + ) + self.updateMetadataForPersistentStore(store) + return store + } + catch let error as NSError where resetStoreOnModelMismatch && error.isCoreDataMigrationError { + + fileManager.removeSQLiteStoreAtURL(fileURL) do { - store = try coordinator.addPersistentStoreWithType( + let store = try coordinator.addPersistentStoreSynchronously( NSSQLiteStoreType, configuration: configuration, URL: fileURL, options: options ) + self.updateMetadataForPersistentStore(store) + return store } catch { - storeError = error as NSError + CoreStore.handleError( + error as NSError, + "Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"." + ) + throw error } } - - if let store = store { + catch { - self.updateMetadataForPersistentStore(store) - return store + CoreStore.handleError( + error as NSError, + "Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"." + ) + throw error } - - if let error = storeError - where (resetStoreOnModelMismatch && error.isCoreDataMigrationError) { - - fileManager.removeSQLiteStoreAtURL(fileURL) - - var store: NSPersistentStore? - coordinator.performBlockAndWait { - - do { - - store = try coordinator.addPersistentStoreWithType( - NSSQLiteStoreType, - configuration: configuration, - URL: fileURL, - options: [NSSQLitePragmasOption: ["journal_mode": "WAL"]] - ) - } - catch { - - storeError = error as NSError - } - } - - if let store = store { - - self.updateMetadataForPersistentStore(store) - return store - } - } - - let error = storeError ?? NSError(coreStoreErrorCode: .UnknownError) - CoreStore.handleError( - error, - "Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"." - ) - throw error } @@ -399,9 +368,7 @@ public final class DataStack { deinit { - for store in self.coordinator.persistentStores { - - _ = try? self.coordinator.removePersistentStore(store) - } + let coordinator = self.coordinator + coordinator.persistentStores.forEach { _ = try? coordinator.removePersistentStore($0) } } } From df866718cf83572409097505c08eb1697552c4f9 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Tue, 9 Feb 2016 07:35:31 +0900 Subject: [PATCH 004/108] tidy up --- CoreStore.xcodeproj/project.pbxproj | 4 ++ .../Internal/NSManagedObjectModel+Setup.swift | 4 +- .../NSPersistentStoreCoordinator+Setup.swift | 64 +++++++++++++++++++ CoreStore/Internal/WeakObject.swift | 5 +- CoreStore/Observing/ListMonitor.swift | 2 +- 5 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 CoreStore/Internal/NSPersistentStoreCoordinator+Setup.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index eea6690..afc7d19 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -206,6 +206,7 @@ B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F2D1AFF849C0064E85B /* WeakObject.swift */; }; B56964D41B22FFAD0075EE4A /* DataStack+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56964D31B22FFAD0075EE4A /* DataStack+Migration.swift */; }; B56965241B356B820075EE4A /* MigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56965231B356B820075EE4A /* MigrationResult.swift */; }; + B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */; }; B59D5C221B5BA34B00453479 /* NSFileManager+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59D5C211B5BA34B00453479 /* NSFileManager+Setup.swift */; }; B5A261211B64BFDB006EB6D3 /* MigrationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A261201B64BFDB006EB6D3 /* MigrationType.swift */; }; B5C976E31C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */; }; @@ -319,6 +320,7 @@ B563217B1BD650E3006C9394 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; B56964D31B22FFAD0075EE4A /* DataStack+Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Migration.swift"; sourceTree = ""; }; B56965231B356B820075EE4A /* MigrationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationResult.swift; sourceTree = ""; }; + B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStoreCoordinator+Setup.swift"; sourceTree = ""; }; B59D5C211B5BA34B00453479 /* NSFileManager+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFileManager+Setup.swift"; sourceTree = ""; }; B5A261201B64BFDB006EB6D3 /* MigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationType.swift; sourceTree = ""; }; B5BDC91A1C202269008147CD /* CartFile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CartFile; sourceTree = ""; }; @@ -669,6 +671,7 @@ B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */, B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */, B51BE0691B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift */, + B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */, B5E84F2D1AFF849C0064E85B /* WeakObject.swift */, ); path = Internal; @@ -985,6 +988,7 @@ B5FAD6AC1B51285300714891 /* MigrationManager.swift in Sources */, B5E84EF61AFF846E0064E85B /* DataStack+Transaction.swift in Sources */, B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */, + B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, B5E834BB1B7691F3001D3D50 /* Functions.swift in Sources */, B5E84F231AFF84860064E85B /* ListMonitor.swift in Sources */, B5E84EF71AFF846E0064E85B /* UnsafeDataTransaction.swift in Sources */, diff --git a/CoreStore/Internal/NSManagedObjectModel+Setup.swift b/CoreStore/Internal/NSManagedObjectModel+Setup.swift index ef22705..87fd899 100644 --- a/CoreStore/Internal/NSManagedObjectModel+Setup.swift +++ b/CoreStore/Internal/NSManagedObjectModel+Setup.swift @@ -108,7 +108,7 @@ internal extension NSManagedObjectModel { fatalError("Could not create an \(typeName(NSManagedObjectModel)) from the model at URL \"\(modelFileURL)\".") } - @nonobjc private(set) internal var currentModelVersion: String? { + @nonobjc internal private(set) var currentModelVersion: String? { get { @@ -128,7 +128,7 @@ internal extension NSManagedObjectModel { } } - @nonobjc private(set) internal var modelVersions: Set? { + @nonobjc internal private(set) var modelVersions: Set? { get { diff --git a/CoreStore/Internal/NSPersistentStoreCoordinator+Setup.swift b/CoreStore/Internal/NSPersistentStoreCoordinator+Setup.swift new file mode 100644 index 0000000..d135629 --- /dev/null +++ b/CoreStore/Internal/NSPersistentStoreCoordinator+Setup.swift @@ -0,0 +1,64 @@ +// +// NSPersistentStoreCoordinator+Setup.swift +// CoreStore +// +// Copyright © 2016 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - NSPersistentStoreCoordinator + +internal extension NSPersistentStoreCoordinator { + + // MARK: Internal + + internal func addPersistentStoreSynchronously(storeType: String, configuration: String?, URL storeURL: NSURL?, options: [NSObject : AnyObject]?) throws -> NSPersistentStore { + + var store: NSPersistentStore? + var storeError: NSError? + self.performBlockAndWait { + + do { + + store = try self.addPersistentStoreWithType( + storeType, + configuration: configuration, + URL: storeURL, + options: options + ) + } + catch { + + storeError = error as NSError + } + } + + if let store = store { + + return store + } + + throw storeError ?? NSError(coreStoreErrorCode: .UnknownError) + } +} \ No newline at end of file diff --git a/CoreStore/Internal/WeakObject.swift b/CoreStore/Internal/WeakObject.swift index 3afab96..0b76a45 100644 --- a/CoreStore/Internal/WeakObject.swift +++ b/CoreStore/Internal/WeakObject.swift @@ -37,8 +37,5 @@ internal final class WeakObject { self.object = object } - - // MARK: Private - - private(set) weak var object: AnyObject? + internal private(set) weak var object: AnyObject? } diff --git a/CoreStore/Observing/ListMonitor.swift b/CoreStore/Observing/ListMonitor.swift index fd0f540..b6e3e86 100644 --- a/CoreStore/Observing/ListMonitor.swift +++ b/CoreStore/Observing/ListMonitor.swift @@ -854,7 +854,7 @@ public final class ListMonitor { /** Returns `true` if a call to `refetch(...)` was made to the `ListMonitor` and is currently waiting for the fetching to complete. Returns `false` otherwise. */ - private(set) public var isPendingRefetch = false + public private(set) var isPendingRefetch = false /** Asks the `ListMonitor` to refetch its objects using the specified series of `FetchClause`s. Note that this method does not execute the fetch immediately; the actual fetching will happen after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes. From 2071ce722eb248bb231e36d84032c9f6f171a7b8 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Mon, 1 Feb 2016 01:05:26 +0900 Subject: [PATCH 005/108] WIP: iCloud support --- CoreStore.xcodeproj/project.pbxproj | 18 ++ CoreStore/NSError+CoreStore.swift | 5 + CoreStore/iCloud/DataStack+iCloud.swift | 169 ++++++++++++++++++ .../CoreStoreDemo/Base.lproj/LaunchScreen.xib | 6 +- 4 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 CoreStore/iCloud/DataStack+iCloud.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index afc7d19..5536c4b 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -85,6 +85,10 @@ B519E45A1C4CD2DA00E7B469 /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B519E4571C4CD2CA00E7B469 /* GCDKit.framework */; }; B519E45B1C4CD2ED00E7B469 /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B519E4571C4CD2CA00E7B469 /* GCDKit.framework */; }; B51BE06A1B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51BE0691B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift */; }; + B51F259A1C5747DD0083A5DD /* DataStack+iCloud.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F25991C5747DD0083A5DD /* DataStack+iCloud.swift */; }; + B51F259B1C57875E0083A5DD /* DataStack+iCloud.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F25991C5747DD0083A5DD /* DataStack+iCloud.swift */; }; + B51F259C1C57875F0083A5DD /* DataStack+iCloud.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F25991C5747DD0083A5DD /* DataStack+iCloud.swift */; }; + B51F259D1C5787600083A5DD /* DataStack+iCloud.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51F25991C5747DD0083A5DD /* DataStack+iCloud.swift */; }; B5202CFA1C04688100DED140 /* NSFetchedResultsController+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */; }; B5202CFD1C046E8400DED140 /* NSFetchedResultsController+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */; }; B52DD17E1BE1F8CD00949AFE /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B52DD1741BE1F8CC00949AFE /* CoreStore.framework */; }; @@ -306,6 +310,7 @@ B504D0D51B02362500B2BBB1 /* CoreStore+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+Setup.swift"; sourceTree = ""; }; B519E4571C4CD2CA00E7B469 /* GCDKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GCDKit.framework; path = "../../Library/Developer/Xcode/DerivedData/Build/Products/Debug-iphoneos/GCDKit.framework"; sourceTree = ""; }; B51BE0691B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectModel+Setup.swift"; sourceTree = ""; }; + B51F25991C5747DD0083A5DD /* DataStack+iCloud.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+iCloud.swift"; sourceTree = ""; }; B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFetchedResultsController+Convenience.swift"; sourceTree = ""; }; B52DD1741BE1F8CC00949AFE /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B52DD17D1BE1F8CC00949AFE /* CoreStoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreStoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -478,6 +483,7 @@ 2F291E2619C6D3CF007AF63F /* CoreStore.swift */, B5D1E22B19FA9FBC003B2874 /* NSError+CoreStore.swift */, B5E84EDA1AFF84500064E85B /* Setting Up */, + B51F25981C5747790083A5DD /* iCloud */, B5E84EE21AFF84610064E85B /* Logging */, B5E84EE91AFF846E0064E85B /* Saving and Processing */, B5E834B61B7630BD001D3D50 /* Importing Data */, @@ -540,6 +546,14 @@ name = Frameworks; sourceTree = ""; }; + B51F25981C5747790083A5DD /* iCloud */ = { + isa = PBXGroup; + children = ( + B51F25991C5747DD0083A5DD /* DataStack+iCloud.swift */, + ); + path = iCloud; + sourceTree = ""; + }; B56964D11B22FF700075EE4A /* Migrating */ = { isa = PBXGroup; children = ( @@ -987,6 +1001,7 @@ B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */, B5FAD6AC1B51285300714891 /* MigrationManager.swift in Sources */, B5E84EF61AFF846E0064E85B /* DataStack+Transaction.swift in Sources */, + B51F259A1C5747DD0083A5DD /* DataStack+iCloud.swift in Sources */, B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */, B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, B5E834BB1B7691F3001D3D50 /* Functions.swift in Sources */, @@ -1065,6 +1080,7 @@ 82BA18CD1C4BBD7100A0916E /* AssociatedObjects.swift in Sources */, 82BA18B71C4BBD3F00A0916E /* CoreStore+Querying.swift in Sources */, 82BA18A41C4BBD2200A0916E /* PersistentStoreResult.swift in Sources */, + B51F259B1C57875E0083A5DD /* DataStack+iCloud.swift in Sources */, 82BA18AA1C4BBD3100A0916E /* BaseDataTransaction.swift in Sources */, 82BA18A91C4BBD3100A0916E /* Into.swift in Sources */, 82BA18D11C4BBD7100A0916E /* NotificationObserver.swift in Sources */, @@ -1117,6 +1133,7 @@ B52DD1BF1BE1F94600949AFE /* AssociatedObjects.swift in Sources */, B52DD1A11BE1F92C00949AFE /* DataStack+Transaction.swift in Sources */, B52DD19E1BE1F92C00949AFE /* AsynchronousDataTransaction.swift in Sources */, + B51F259D1C5787600083A5DD /* DataStack+iCloud.swift in Sources */, B52DD1981BE1F92500949AFE /* CoreStore+Setup.swift in Sources */, B52DD1941BE1F92500949AFE /* CoreStore.swift in Sources */, B52DD1A61BE1F92F00949AFE /* BaseDataTransaction+Importing.swift in Sources */, @@ -1208,6 +1225,7 @@ B56321A71BD65216006C9394 /* MigrationResult.swift in Sources */, B56321A11BD65216006C9394 /* ListMonitor.swift in Sources */, B56321881BD65216006C9394 /* BaseDataTransaction.swift in Sources */, + B51F259C1C57875F0083A5DD /* DataStack+iCloud.swift in Sources */, B56321A31BD65216006C9394 /* DataStack+Migration.swift in Sources */, B56321901BD65216006C9394 /* ImportableUniqueObject.swift in Sources */, B56321871BD65216006C9394 /* Into.swift in Sources */, diff --git a/CoreStore/NSError+CoreStore.swift b/CoreStore/NSError+CoreStore.swift index d32a995..f5168d8 100644 --- a/CoreStore/NSError+CoreStore.swift +++ b/CoreStore/NSError+CoreStore.swift @@ -58,6 +58,11 @@ public enum CoreStoreErrorCode: Int { An `NSMappingModel` could not be found for a specific source and destination model versions. */ case MappingModelNotFound + + /** + The container could not be located or if iCloud storage is unavailable for the current user or device. + */ + case ICloudContainerNotFound } diff --git a/CoreStore/iCloud/DataStack+iCloud.swift b/CoreStore/iCloud/DataStack+iCloud.swift new file mode 100644 index 0000000..fad2ebc --- /dev/null +++ b/CoreStore/iCloud/DataStack+iCloud.swift @@ -0,0 +1,169 @@ +// +// DataStack+iCloud.swift +// CoreStore +// +// Copyright © 2016 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData +#if USE_FRAMEWORKS + import GCDKit +#endif + +public extension DataStack { + + public func addICloudStore(ubiquitousContentName: String, ubiquitousContentURLRelativePath: String? = nil, ubiquitousContainerID: String? = nil, ubiquitousPeerToken: String? = nil, configuration: String? = nil, mappingModelBundles: [NSBundle]? = NSBundle.allBundles(), automigrating: Bool, resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { + + CoreStore.assert( + !ubiquitousContentName.isEmpty, + "The ubiquitousContentName cannot be empty." + ) + CoreStore.assert( + !ubiquitousContentName.containsString("."), + "The ubiquitousContentName cannot contain periods." + ) + CoreStore.assert( + ubiquitousContentURLRelativePath?.isEmpty != true, + "The ubiquitousContentURLRelativePath should not be empty if provided." + ) + CoreStore.assert( + ubiquitousPeerToken?.isEmpty != true, + "The ubiquitousPeerToken should not be empty if provided." + ) + + let fileManager = NSFileManager.defaultManager() + guard let fileURL = fileManager.URLForUbiquityContainerIdentifier(ubiquitousContainerID) else { + + throw NSError(coreStoreErrorCode: .ICloudContainerNotFound) + } + + let coordinator = self.coordinator; + if let store = coordinator.persistentStoreForURL(fileURL) { + + guard store.type == NSSQLiteStoreType + && store.configurationName == (configuration ?? Into.defaultConfigurationName) else { + + let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL) + CoreStore.handleError( + error, + "Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists." + ) + throw error + } + + GCDQueue.Main.async { + + completion(PersistentStoreResult(store)) + } + return nil + } + + _ = try? fileManager.createDirectoryAtURL( + fileURL.URLByDeletingLastPathComponent!, + withIntermediateDirectories: true, + attributes: nil + ) + + var options = self.optionsForSQLiteStore() + options[NSPersistentStoreUbiquitousContentNameKey] = ubiquitousContentName + options[NSMigratePersistentStoresAutomaticallyOption] = automigrating + options[NSInferMappingModelAutomaticallyOption] = automigrating + + if let ubiquitousContentURLRelativePath = ubiquitousContentURLRelativePath { + + options[NSPersistentStoreUbiquitousContentURLKey] = ubiquitousContentURLRelativePath + } + if let ubiquitousContainerID = ubiquitousContainerID { + + options[NSPersistentStoreUbiquitousContainerIdentifierKey] = ubiquitousContainerID + } + if let ubiquitousPeerToken = ubiquitousPeerToken { + + options[NSPersistentStoreUbiquitousPeerTokenOption] = ubiquitousPeerToken + } + + var store: NSPersistentStore? + var storeError: NSError? + coordinator.performBlockAndWait { + + do { + + store = try coordinator.addPersistentStoreWithType( + NSSQLiteStoreType, + configuration: configuration, + URL: fileURL, + options: options + ) + } + catch { + + storeError = error as NSError + } + } + + if let store = store { + + self.updateMetadataForPersistentStore(store) + return store + } + + if let error = storeError + where (resetStoreOnModelMismatch && error.isCoreDataMigrationError) { + + fileManager.removeSQLiteStoreAtURL(fileURL) + + var store: NSPersistentStore? + coordinator.performBlockAndWait { + + do { + + store = try coordinator.addPersistentStoreWithType( + NSSQLiteStoreType, + configuration: configuration, + URL: fileURL, + options: options + ) + } + catch { + + storeError = error as NSError + } + } + + if let store = store { + + self.updateMetadataForPersistentStore(store) + return store + } + } + + let error = storeError ?? NSError(coreStoreErrorCode: .UnknownError) + CoreStore.handleError( + error, + "Failed to add SQLite \(typeName(NSPersistentStore)) at \"\(fileURL)\"." + ) + throw error + + + + } +} diff --git a/CoreStoreDemo/CoreStoreDemo/Base.lproj/LaunchScreen.xib b/CoreStoreDemo/CoreStoreDemo/Base.lproj/LaunchScreen.xib index c3e905c..2aec9f6 100644 --- a/CoreStoreDemo/CoreStoreDemo/Base.lproj/LaunchScreen.xib +++ b/CoreStoreDemo/CoreStoreDemo/Base.lproj/LaunchScreen.xib @@ -1,7 +1,7 @@ - + - + @@ -11,7 +11,7 @@ - - + + + + + + diff --git a/CoreStoreDemo/CoreStoreDemo/Base.lproj/LaunchScreen.xib.orig b/CoreStoreDemo/CoreStoreDemo/Base.lproj/LaunchScreen.xib.orig new file mode 100644 index 0000000..5948784 --- /dev/null +++ b/CoreStoreDemo/CoreStoreDemo/Base.lproj/LaunchScreen.xib.orig @@ -0,0 +1,53 @@ + +<<<<<<< Updated upstream + + + +======= + + + + +>>>>>>> Stashed changes + + + + + + + + + +<<<<<<< Updated upstream + + + + + + + + + + + + + + + + diff --git a/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard b/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard index 1a2c4ed..38842c2 100644 --- a/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard +++ b/CoreStoreDemo/CoreStoreDemo/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -297,7 +297,7 @@