diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index b87829e..9ff0e22 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -282,6 +282,10 @@ B5FE4DAD1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; }; B5FE4DAE1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; }; B5FE4DAF1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; }; + B5FEC18E1C9166E200532541 /* NSPersistentStore+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */; }; + B5FEC18F1C9166E600532541 /* NSPersistentStore+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */; }; + B5FEC1901C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */; }; + B5FEC1911C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -403,6 +407,7 @@ B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageInterface.swift; sourceTree = ""; }; B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InMemoryStore.swift; sourceTree = ""; }; B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteStore.swift; sourceTree = ""; }; + B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStore+Setup.swift"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -696,17 +701,18 @@ isa = PBXGroup; children = ( B5E84F2A1AFF849C0064E85B /* AssociatedObjects.swift */, + B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */, B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */, B5E834BA1B7691F3001D3D50 /* Functions.swift */, B5FAD6AB1B51285300714891 /* MigrationManager.swift */, B5E84F2B1AFF849C0064E85B /* NotificationObserver.swift */, B59D5C211B5BA34B00453479 /* NSFileManager+Setup.swift */, - B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */, B5E84F2C1AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift */, B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */, B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */, B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */, B51BE0691B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift */, + B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */, B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */, B5E84F2D1AFF849C0064E85B /* WeakObject.swift */, ); @@ -1037,6 +1043,7 @@ B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */, B5FAD6AC1B51285300714891 /* MigrationManager.swift in Sources */, B5E84EF61AFF846E0064E85B /* DataStack+Transaction.swift in Sources */, + B5FEC18E1C9166E200532541 /* NSPersistentStore+Setup.swift in Sources */, B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */, B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, B5E834BB1B7691F3001D3D50 /* Functions.swift in Sources */, @@ -1119,6 +1126,7 @@ 82BA18C61C4BBD5900A0916E /* DataStack+Migration.swift in Sources */, 82BA18CD1C4BBD7100A0916E /* AssociatedObjects.swift in Sources */, B59851491C90289D00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, + B5FEC18F1C9166E600532541 /* NSPersistentStore+Setup.swift in Sources */, 82BA18B71C4BBD3F00A0916E /* CoreStore+Querying.swift in Sources */, 82BA18A41C4BBD2200A0916E /* SetupResult.swift in Sources */, 82BA18AA1C4BBD3100A0916E /* BaseDataTransaction.swift in Sources */, @@ -1169,6 +1177,7 @@ B52DD1BE1BE1F94300949AFE /* NSProgress+Convenience.swift in Sources */, B52DD1951BE1F92500949AFE /* NSError+CoreStore.swift in Sources */, B52DD1C21BE1F94600949AFE /* MigrationManager.swift in Sources */, + B5FEC1911C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */, B52DD1AB1BE1F93900949AFE /* From.swift in Sources */, B52DD1BF1BE1F94600949AFE /* AssociatedObjects.swift in Sources */, B52DD1A11BE1F92C00949AFE /* DataStack+Transaction.swift in Sources */, @@ -1272,6 +1281,7 @@ B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */, B56321A71BD65216006C9394 /* MigrationResult.swift in Sources */, B598514A1C90289E00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, + B5FEC1901C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */, B56321A11BD65216006C9394 /* ListMonitor.swift in Sources */, B56321881BD65216006C9394 /* BaseDataTransaction.swift in Sources */, B56321A31BD65216006C9394 /* DataStack+Migration.swift in Sources */, diff --git a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift index 024a867..8cf7e74 100644 --- a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift @@ -50,22 +50,21 @@ class MigrationsDemoViewController: UIViewController { (dataStack: DataStack) -> ModelMetadata in let models = self.models - do { + let migrations = try! dataStack.requiredMigrationsForStorage( + SQLiteStore(fileName: "MigrationDemo.sqlite") + ) + + guard let storeVersion = migrations.first?.sourceVersion else { + + return models.first! + } + for model in models { - let migrations = try dataStack.requiredMigrationsForStorage( - SQLiteStore(fileName: "MigrationDemo.sqlite") - ) - - let storeVersion = migrations.first?.sourceVersion ?? dataStack.modelVersion - for model in models { + if model.version == storeVersion { - if model.version == storeVersion { - - return model - } + return model } } - catch _ { } return models.first! } diff --git a/Sources/Internal/NSPersistentStore+Setup.swift b/Sources/Internal/NSPersistentStore+Setup.swift new file mode 100644 index 0000000..002b896 --- /dev/null +++ b/Sources/Internal/NSPersistentStore+Setup.swift @@ -0,0 +1,78 @@ +// +// NSPersistentStore+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: - NSPersistentStore + +internal extension NSPersistentStore { + + // MARK: Internal + + @nonobjc internal var storageInterface: StorageInterface? { + + get { + + let wrapper: StorageObject? = getAssociatedObjectForKey( + &PropertyKeys.storageInterface, + inObject: self + ) + return wrapper?.storageInterface + } + set { + + setAssociatedRetainedObject( + StorageObject(newValue), + forKey: &PropertyKeys.storageInterface, + inObject: self + ) + } + } + + + // MARK: Private + + private struct PropertyKeys { + + static var storageInterface: Void? + } +} + + +// MARK: - StorageObject + +private class StorageObject: NSObject { + + // MARK: Private + + private let storageInterface: StorageInterface? + + private init(_ storage: StorageInterface?) { + + self.storageInterface = storage + } +} diff --git a/Sources/Migrating/DataStack+Migration.swift b/Sources/Migrating/DataStack+Migration.swift index 5cf7a0d..5a4317e 100644 --- a/Sources/Migrating/DataStack+Migration.swift +++ b/Sources/Migrating/DataStack+Migration.swift @@ -41,39 +41,20 @@ public extension DataStack { public func addStorage(storage: T, completion: (SetupResult) -> Void) throws -> NSProgress? { - // TODO: check - let coordinator = self.coordinator; -// if let persistentStore = coordinator.persistentStoreForURL(fileURL) { -// -// guard persistentStore.type == storage.dynamicType.storeType -// && persistentStore.configurationName == (storage.configuration ?? Into.defaultConfigurationName) else { -// -// let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL) -// CoreStore.handleError( -// error, -// "Failed to add SQLite \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists." -// ) -// throw error -// } -// -// GCDQueue.Main.async { -// -// completion(SetupResult(storage)) -// } -// return nil -// } - - coordinator.performBlock { + self.coordinator.performBlock { + + if let _ = self.persistentStoreForStorage(storage) { + + GCDQueue.Main.async { + + completion(SetupResult(storage)) + } + return + } do { - let persistentStore = try coordinator.addPersistentStoreWithType( - storage.dynamicType.storeType, - configuration: storage.configuration, - URL: nil, - options: storage.storeOptions - ) - self.updateMetadataForStorage(storage, persistentStore: persistentStore) + try self.createPersistentStoreFromStorage(storage, finalURL: nil) GCDQueue.Main.async { @@ -121,105 +102,113 @@ public extension DataStack { "The specified URL for the \(typeName(storage)) is invalid: \"\(fileURL)\"" ) - // TODO: check - let coordinator = self.coordinator; - if let persistentStore = coordinator.persistentStoreForURL(fileURL) { + return try self.coordinator.performBlockAndWait { - guard persistentStore.type == storage.dynamicType.storeType - && persistentStore.configurationName == (storage.configuration ?? Into.defaultConfigurationName) else { - - let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL) - CoreStore.handleError( - error, - "Failed to add \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists." - ) - throw error - } - - GCDQueue.Main.async { - - completion(SetupResult(storage)) - } - return nil - } - - let fileManager = NSFileManager.defaultManager() - - do { - - try fileManager.createDirectoryAtURL( - fileURL.URLByDeletingLastPathComponent!, - withIntermediateDirectories: true, - attributes: nil - ) - - let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( - storage.dynamicType.storeType, - URL: fileURL, - options: storage.storeOptions - ) - - return self.upgradeStorageIfNeeded( - storage, - metadata: metadata, - completion: { (result) -> Void in - - if case .Failure(let error) = result { - - if storage.resetStoreOnModelMismatch && error.isCoreDataMigrationError { - - do { - - try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait) - try self.addStorageAndWait(storage) - - GCDQueue.Main.async { - - completion(SetupResult(storage)) - } - } - catch { - - completion(SetupResult(error as NSError)) - } - return - } - - completion(SetupResult(error)) - return - } - - do { - - try self.addStorageAndWait(storage) - - completion(SetupResult(storage)) - } - catch { - - completion(SetupResult(error as NSError)) - } - } - ) - } - catch let error as NSError - where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain { - - try self.addStorageAndWait(storage) + if let _ = self.persistentStoreForStorage(storage) { GCDQueue.Main.async { completion(SetupResult(storage)) } return nil - } - catch { + } - CoreStore.handleError( - error as NSError, - "Failed to load SQLite \(typeName(NSPersistentStore)) metadata." - ) - throw error + if let persistentStore = self.coordinator.persistentStoreForURL(fileURL) { + + if let existingStorage = persistentStore.storageInterface as? T + where storage.matchesPersistentStore(persistentStore) { + + GCDQueue.Main.async { + + completion(SetupResult(existingStorage)) + } + return nil + } + + let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL) + CoreStore.handleError( + error, + "Failed to add \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists." + ) + throw error + } + + do { + + try NSFileManager.defaultManager().createDirectoryAtURL( + fileURL.URLByDeletingLastPathComponent!, + withIntermediateDirectories: true, + attributes: nil + ) + + let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( + storage.dynamicType.storeType, + URL: fileURL, + options: storage.storeOptions + ) + + return self.upgradeStorageIfNeeded( + storage, + metadata: metadata, + completion: { (result) -> Void in + + if case .Failure(let error) = result { + + if storage.resetStoreOnModelMismatch && error.isCoreDataMigrationError { + + do { + + try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait) + try self.addStorageAndWait(storage) + + GCDQueue.Main.async { + + completion(SetupResult(storage)) + } + } + catch { + + completion(SetupResult(error as NSError)) + } + return + } + + completion(SetupResult(error)) + return + } + + do { + + try self.addStorageAndWait(storage) + + completion(SetupResult(storage)) + } + catch { + + completion(SetupResult(error as NSError)) + } + } + ) + } + catch let error as NSError + where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain { + + try self.addStorageAndWait(storage) + + GCDQueue.Main.async { + + completion(SetupResult(storage)) + } + return nil + } + catch { + + CoreStore.handleError( + error as NSError, + "Failed to load SQLite \(typeName(NSPersistentStore)) metadata." + ) + throw error + } } } @@ -234,30 +223,36 @@ public extension DataStack { */ public func upgradeStorageIfNeeded(storage: T, completion: (MigrationResult) -> Void) throws -> NSProgress? { - let fileURL = storage.fileURL - let metadata: [String: AnyObject] - do { + return try self.coordinator.performBlockAndWait { - metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( - storage.dynamicType.storeType, - URL: fileURL, - options: storage.storeOptions - ) + let fileURL = storage.fileURL + do { + + CoreStore.assert( + self.persistentStoreForStorage(storage) == nil, + "Attempted to migrate an already added \(typeName(storage)) at URL \"\(fileURL)\"" + ) + + let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( + storage.dynamicType.storeType, + URL: fileURL, + options: storage.storeOptions + ) + return self.upgradeStorageIfNeeded( + storage, + metadata: metadata, + completion: completion + ) + } + catch { + + CoreStore.handleError( + error as NSError, + "Failed to load \(typeName(storage)) metadata from URL \"\(fileURL)\"." + ) + throw error + } } - catch { - - CoreStore.handleError( - error as NSError, - "Failed to load \(typeName(storage)) metadata from URL \"\(fileURL)\"." - ) - throw error - } - - return self.upgradeStorageIfNeeded( - storage, - metadata: metadata, - completion: completion - ) } @@ -272,36 +267,48 @@ public extension DataStack { @warn_unused_result public func requiredMigrationsForStorage(storage: T) throws -> [MigrationType] { - let fileURL = storage.fileURL - let metadata: [String : AnyObject] - do { + return try self.coordinator.performBlockAndWait { - metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( - storage.dynamicType.storeType, - URL: fileURL, - options: storage.storeOptions - ) - } - catch { + let fileURL = storage.fileURL - CoreStore.handleError( - error as NSError, - "Failed to load \(typeName(storage)) metadata from URL \"\(fileURL)\"." + CoreStore.assert( + self.persistentStoreForStorage(storage) == nil, + "Attempted to query required migrations for an already added \(typeName(storage)) at URL \"\(fileURL)\"" ) - throw error + do { + + let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( + storage.dynamicType.storeType, + URL: fileURL, + options: storage.storeOptions + ) + + guard let migrationSteps = self.computeMigrationFromStorageMetadata(metadata, configuration: storage.configuration, mappingModelBundles: storage.mappingModelBundles) else { + + let error = NSError(coreStoreErrorCode: .MappingModelNotFound) + CoreStore.handleError( + error, + "Failed to find migration steps from the \(typeName(storage)) at URL \"\(fileURL)\" to version model \"\(self.modelVersion)\"." + ) + throw error + } + + return migrationSteps.map { $0.migrationType } + } + catch let error as NSError + where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain { + + return [] + } + catch { + + CoreStore.handleError( + error as NSError, + "Failed to load \(typeName(storage)) metadata from URL \"\(fileURL)\"." + ) + throw error + } } - - guard let migrationSteps = self.computeMigrationFromStorageMetadata(metadata, configuration: storage.configuration, mappingModelBundles: storage.mappingModelBundles) else { - - let error = NSError(coreStoreErrorCode: .MappingModelNotFound) - CoreStore.handleError( - error, - "Failed to find migration steps from the \(typeName(storage)) at URL \"\(fileURL)\" to version model \"\(self.modelVersion)\"." - ) - throw error - } - - return migrationSteps.map { $0.migrationType } } diff --git a/Sources/Setting Up/CoreStore+Setup.swift b/Sources/Setting Up/CoreStore+Setup.swift index cc6d77d..673c907 100644 --- a/Sources/Setting Up/CoreStore+Setup.swift +++ b/Sources/Setting Up/CoreStore+Setup.swift @@ -120,7 +120,7 @@ public extension CoreStore { ``` - parameter storage: the local storage - - returns: the local storage added to the `defaultStack` + - returns: the local storage added to the `defaultStack`. Note that this may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration. */ public static func addStorageAndWait(storage: T) throws -> T { diff --git a/Sources/Setting Up/DataStack.swift b/Sources/Setting Up/DataStack.swift index 6288c6b..3c22f3f 100644 --- a/Sources/Setting Up/DataStack.swift +++ b/Sources/Setting Up/DataStack.swift @@ -149,21 +149,18 @@ public final class DataStack { */ public func addStorageAndWait(storage: T) throws -> T { - CoreStore.assert( - storage.internalStore == nil, - "The specified \"\(typeName(storage))\" was already added to the data stack: \(storage)" - ) - do { - let persistentStore = try self.coordinator.addPersistentStoreSynchronously( - storage.dynamicType.storeType, - configuration: storage.configuration, - URL: nil, - options: storage.storeOptions - ) - self.updateMetadataForStorage(storage, persistentStore: persistentStore) - return storage + return try self.coordinator.performBlockAndWait { () -> T in + + if let _ = self.persistentStoreForStorage(storage) { + + return storage + } + + try self.createPersistentStoreFromStorage(storage, finalURL: nil) + return storage + } } catch { @@ -196,64 +193,50 @@ public final class DataStack { ``` - parameter storage: the local storage - - returns: the local storage added to the stack + - returns: the local storage added to the stack. Note that this may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration. */ public func addStorageAndWait(storage: T) throws -> T { - CoreStore.assert( - storage.internalStore == nil, - "The specified \"\(typeName(storage))\" was already added to the data stack: \(storage)" - ) - - let fileURL = storage.fileURL - CoreStore.assert( - fileURL.fileURL, - "The specified store URL for the \"\(typeName(storage))\" is invalid: \"\(fileURL)\"" - ) - - if let persistentStore = coordinator.persistentStoreForURL(fileURL) { - - guard persistentStore.type == storage.dynamicType.storeType - && persistentStore.configurationName == (storage.configuration ?? Into.defaultConfigurationName) else { - - let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL) - CoreStore.handleError( - error, - "Failed to add \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists." - ) - throw error + return try self.coordinator.performBlockAndWait { + + let fileURL = storage.fileURL + CoreStore.assert( + fileURL.fileURL, + "The specified store URL for the \"\(typeName(storage))\" is invalid: \"\(fileURL)\"" + ) + + if let _ = self.persistentStoreForStorage(storage) { + + return storage } - storage.internalStore = persistentStore - return storage - } - - do { - - let coordinator = self.coordinator - return try coordinator.performBlockAndWait { + if let persistentStore = self.coordinator.persistentStoreForURL(fileURL) { - let addStorage = { () throws -> T in + if let existingStorage = persistentStore.storageInterface as? T + where storage.matchesPersistentStore(persistentStore) { - let persistentStore = try coordinator.addPersistentStoreWithType( - storage.dynamicType.storeType, - configuration: storage.configuration, - URL: fileURL, - options: storage.storeOptions - ) - self.updateMetadataForStorage(storage, persistentStore: persistentStore) - return storage + return existingStorage } - let fileManager = NSFileManager.defaultManager() + let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL) + CoreStore.handleError( + error, + "Failed to add \(typeName(storage)) at \"\(fileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists." + ) + throw error + } + + do { + do { - try fileManager.createDirectoryAtURL( + try NSFileManager.defaultManager().createDirectoryAtURL( fileURL.URLByDeletingLastPathComponent!, withIntermediateDirectories: true, attributes: nil ) - return try addStorage() + try self.createPersistentStoreFromStorage(storage, finalURL: fileURL) + return storage } catch let error as NSError where storage.resetStoreOnModelMismatch && error.isCoreDataMigrationError { @@ -264,17 +247,18 @@ public final class DataStack { ) try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait) - return try addStorage() + try self.createPersistentStoreFromStorage(storage, finalURL: fileURL) + return storage } } - } - catch { - - CoreStore.handleError( - error as NSError, - "Failed to add \(typeName(storage)) to the stack." - ) - throw error + catch { + + CoreStore.handleError( + error as NSError, + "Failed to add \(typeName(storage)) to the stack." + ) + throw error + } } } @@ -300,6 +284,13 @@ public final class DataStack { return migrationQueue }() + internal func persistentStoreForStorage(storage: StorageInterface) -> NSPersistentStore? { + + return self.coordinator.persistentStores + .filter { $0.storageInterface === storage } + .first + } + internal func entityNameForEntityClass(entityClass: AnyClass) -> String? { return self.model.entityNameForClass(entityClass) @@ -352,9 +343,15 @@ public final class DataStack { return returnValue } - internal func updateMetadataForStorage(storage: StorageInterface, persistentStore: NSPersistentStore) { + internal func createPersistentStoreFromStorage(storage: StorageInterface, finalURL: NSURL?) throws -> NSPersistentStore { - storage.internalStore = persistentStore + let persistentStore = try self.coordinator.addPersistentStoreWithType( + storage.dynamicType.storeType, + configuration: storage.configuration, + URL: finalURL, + options: storage.storeOptions + ) + persistentStore.storageInterface = storage self.storeMetadataUpdateQueue.barrierAsync { @@ -375,6 +372,7 @@ public final class DataStack { self.entityConfigurationsMapping[managedObjectClassName]?.insert(configurationName) } } + return persistentStore } diff --git a/Sources/Setting Up/SetupResult.swift b/Sources/Setting Up/SetupResult.swift index db50f83..babdef2 100644 --- a/Sources/Setting Up/SetupResult.swift +++ b/Sources/Setting Up/SetupResult.swift @@ -29,7 +29,6 @@ import CoreData // MARK: - SetupResult - /** The `SetupResult` indicates the result of an asynchronous initialization of a persistent store. The `SetupResult` can be treated as a boolean: @@ -43,7 +42,7 @@ import CoreData else { // failed } - }n + } ) ``` or as an `enum`, where the resulting associated object can also be inspected: @@ -114,18 +113,18 @@ public enum SetupResult: BooleanType { /** - Deprecated. Replaced by `SetupResult` by using the new `addStorage(_:completion:)` method variants. + Deprecated. Replaced by `SetupResult` when using the new `addStorage(_:completion:)` method variants. */ @available(*, deprecated=2.0.0, message="Replaced by SetupResult by using the new addStorage(_:completion:) method variants.") public enum PersistentStoreResult: BooleanType { /** - Deprecated. Replaced by `SetupResult.Success` by using the new `addStorage(_:completion:)` method variants. + Deprecated. Replaced by `SetupResult.Success` when using the new `addStorage(_:completion:)` method variants. */ case Success(NSPersistentStore) /** - Deprecated. Replaced by `SetupResult.Failure` by using the new `addStorage(_:completion:)` method variants. + Deprecated. Replaced by `SetupResult.Failure` when using the new `addStorage(_:completion:)` method variants. */ case Failure(NSError) diff --git a/Sources/Setting Up/StorageInterfaces/StorageInterface.swift b/Sources/Setting Up/StorageInterfaces/StorageInterface.swift index a2630bd..f37b341 100644 --- a/Sources/Setting Up/StorageInterfaces/StorageInterface.swift +++ b/Sources/Setting Up/StorageInterfaces/StorageInterface.swift @@ -34,8 +34,6 @@ public protocol StorageInterface: class { var configuration: String? { get } var storeOptions: [String: AnyObject]? { get } - - var internalStore: NSPersistentStore? { get set } } @@ -57,3 +55,16 @@ public protocol LocalStorage: StorageInterface { func eraseStorageAndWait(soureModel soureModel: NSManagedObjectModel) throws } + + +// MARK: Internal + +internal extension LocalStorage { + + internal func matchesPersistentStore(persistentStore: NSPersistentStore) -> Bool { + + return persistentStore.type == self.dynamicType.storeType + && persistentStore.configurationName == (self.configuration ?? Into.defaultConfigurationName) + && persistentStore.URL == self.fileURL + } +}