From 99189d160f9aad7059208607d75faf5e3da69e79 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Wed, 2 Mar 2016 08:02:33 +0900 Subject: [PATCH] WIP: Storage protocol --- CoreStore.xcodeproj/project.pbxproj | 10 ++ ...FetchedResultsController+Convenience.swift | 31 ++-- .../AsynchronousDataTransaction.swift | 36 ++--- .../CoreStore+Transaction.swift | 3 + .../DataStack+Transaction.swift | 3 + .../SynchronousDataTransaction.swift | 48 +++--- .../UnsafeDataTransaction.swift | 27 ++-- CoreStore/Setting Up/CoreStore+Setup.swift | 30 +++- CoreStore/Setting Up/DataStack.swift | 37 +++-- .../PersistentStores/InMemoryStore.swift | 16 +- .../PersistentStores/SQLiteStore.swift | 139 ++++++++++++++++++ .../Setting Up/PersistentStores/Storage.swift | 53 ++++++- 12 files changed, 338 insertions(+), 95 deletions(-) create mode 100644 CoreStore/Setting Up/PersistentStores/SQLiteStore.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 87e2684..614e8a4 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -271,6 +271,10 @@ B5FE4DA81C84FB4400FA6A91 /* InMemoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */; }; B5FE4DA91C84FB4400FA6A91 /* InMemoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */; }; B5FE4DAA1C84FB4400FA6A91 /* InMemoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */; }; + B5FE4DAC1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */; }; + 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 */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -389,6 +393,7 @@ B5FAD6AD1B518DCB00714891 /* CoreStore+Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStore+Migration.swift"; sourceTree = ""; }; B5FE4DA11C8481E100FA6A91 /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.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 = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -703,6 +708,7 @@ children = ( B5FE4DA11C8481E100FA6A91 /* Storage.swift */, B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */, + B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */, ); path = PersistentStores; sourceTree = ""; @@ -1013,6 +1019,7 @@ B5E84F2F1AFF849C0064E85B /* NotificationObserver.swift in Sources */, B5F1DA8D1B9AA97D007C5CBB /* ImportableObject.swift in Sources */, B56965241B356B820075EE4A /* MigrationResult.swift in Sources */, + B5FE4DAC1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */, 2F291E2719C6D3CF007AF63F /* CoreStore.swift in Sources */, B5E84F411AFF8CCD0064E85B /* ClauseTypes.swift in Sources */, B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */, @@ -1072,6 +1079,7 @@ 82BA18AE1C4BBD3100A0916E /* DataStack+Transaction.swift in Sources */, 82BA18AB1C4BBD3100A0916E /* AsynchronousDataTransaction.swift in Sources */, 82BA18CE1C4BBD7100A0916E /* FetchedResultsControllerDelegate.swift in Sources */, + B5FE4DAD1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */, 82BA18C51C4BBD5300A0916E /* ListObserver.swift in Sources */, 82BA18C21C4BBD5300A0916E /* ObjectMonitor.swift in Sources */, 82BA18A51C4BBD2200A0916E /* CoreStore+Setup.swift in Sources */, @@ -1160,6 +1168,7 @@ B52DD1AD1BE1F93900949AFE /* Where.swift in Sources */, B52DD1C41BE1F94600949AFE /* NSFileManager+Setup.swift in Sources */, B52DD1AC1BE1F93900949AFE /* Select.swift in Sources */, + B5FE4DAF1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */, B52DD1971BE1F92500949AFE /* PersistentStoreResult.swift in Sources */, B52DD1C71BE1F94600949AFE /* NSManagedObjectContext+Querying.swift in Sources */, B52DD1C81BE1F94600949AFE /* NSManagedObjectContext+Setup.swift in Sources */, @@ -1219,6 +1228,7 @@ B563219D1BD65216006C9394 /* DataStack+Observing.swift in Sources */, B56321961BD65216006C9394 /* From.swift in Sources */, B56321AA1BD6521C006C9394 /* AssociatedObjects.swift in Sources */, + B5FE4DAE1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */, B563218C1BD65216006C9394 /* DataStack+Transaction.swift in Sources */, B563219E1BD65216006C9394 /* CoreStore+Observing.swift in Sources */, B56321891BD65216006C9394 /* AsynchronousDataTransaction.swift in Sources */, diff --git a/CoreStore/Convenience Helpers/NSFetchedResultsController+Convenience.swift b/CoreStore/Convenience Helpers/NSFetchedResultsController+Convenience.swift index 6dd1452..410b297 100644 --- a/CoreStore/Convenience Helpers/NSFetchedResultsController+Convenience.swift +++ b/CoreStore/Convenience Helpers/NSFetchedResultsController+Convenience.swift @@ -45,6 +45,23 @@ public extension NSFetchedResultsController { ) } + + // MARK: Internal + + internal static func createFromContext(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController { + + return CoreStoreFetchedResultsController( + context: context, + fetchRequest: fetchRequest, + from: from, + sectionBy: sectionBy, + fetchClauses: fetchClauses + ) + } + + + // MARK: Deprecated + @available(*, deprecated=1.5.2, message="Use NSFetchedResultsController.createForStack(_:fetchRequest:from:sectionBy:fetchClauses:) to create NSFetchedResultsControllers directly") public convenience init(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) { @@ -75,18 +92,4 @@ public extension NSFetchedResultsController { cacheName: nil ) } - - - // MARK: Internal - - internal static func createFromContext(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController { - - return CoreStoreFetchedResultsController( - context: context, - fetchRequest: fetchRequest, - from: from, - sectionBy: sectionBy, - fetchClauses: fetchClauses - ) - } } diff --git a/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift b/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift index 8863cff..607824a 100644 --- a/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift +++ b/CoreStore/Saving and Processing/AsynchronousDataTransaction.swift @@ -189,24 +189,6 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { super.delete(objects) } - /** - 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, 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( - !self.isCommitted, - "Attempted to rollback an already committed \(typeName(self))." - ) - CoreStore.assert( - self.transactionQueue.isCurrentExecutionContext(), - "Attempted to rollback a \(typeName(self)) outside its designated queue." - ) - - self.context.reset() - } - // MARK: Internal @@ -253,4 +235,22 @@ public final class AsynchronousDataTransaction: BaseDataTransaction { // MARK: Private private let closure: (transaction: AsynchronousDataTransaction) -> Void + + + // MARK: Deprecated + + @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( + !self.isCommitted, + "Attempted to rollback an already committed \(typeName(self))." + ) + CoreStore.assert( + self.transactionQueue.isCurrentExecutionContext(), + "Attempted to rollback a \(typeName(self)) outside its designated queue." + ) + + self.context.reset() + } } diff --git a/CoreStore/Saving and Processing/CoreStore+Transaction.swift b/CoreStore/Saving and Processing/CoreStore+Transaction.swift index 873a264..a0c5cf5 100644 --- a/CoreStore/Saving and Processing/CoreStore+Transaction.swift +++ b/CoreStore/Saving and Processing/CoreStore+Transaction.swift @@ -71,6 +71,9 @@ public extension CoreStore { self.defaultStack.refreshAllObjectsAsFaults() } + + // MARK: Deprecated + @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 7f565be..7277180 100644 --- a/CoreStore/Saving and Processing/DataStack+Transaction.swift +++ b/CoreStore/Saving and Processing/DataStack+Transaction.swift @@ -93,6 +93,9 @@ public extension DataStack { self.mainContext.refreshAllObjectsAsFaults() } + + // MARK: Deprecated + @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 2f61bed..56b10db 100644 --- a/CoreStore/Saving and Processing/SynchronousDataTransaction.swift +++ b/CoreStore/Saving and Processing/SynchronousDataTransaction.swift @@ -184,30 +184,6 @@ public final class SynchronousDataTransaction: BaseDataTransaction { super.delete(objects) } - /** - 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, 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( - !self.isCommitted, - "Attempted to rollback an already committed \(typeName(self))." - ) - CoreStore.assert( - self.transactionQueue.isCurrentExecutionContext(), - "Attempted to rollback a \(typeName(self)) outside its designated queue." - ) - - self.context.reset() - } - - @available(*, deprecated=1.5.2, renamed="commitAndWait") - public func commit() { - - self.commitAndWait() - } - // MARK: Internal @@ -239,4 +215,28 @@ public final class SynchronousDataTransaction: BaseDataTransaction { // MARK: Private private let closure: (transaction: SynchronousDataTransaction) -> Void + + + // MARK: Deprecated + + @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( + !self.isCommitted, + "Attempted to rollback an already committed \(typeName(self))." + ) + CoreStore.assert( + self.transactionQueue.isCurrentExecutionContext(), + "Attempted to rollback a \(typeName(self)) outside its designated queue." + ) + + self.context.reset() + } + + @available(*, deprecated=1.5.2, renamed="commitAndWait") + public func commit() { + + self.commitAndWait() + } } diff --git a/CoreStore/Saving and Processing/UnsafeDataTransaction.swift b/CoreStore/Saving and Processing/UnsafeDataTransaction.swift index f18bad9..5a08a44 100644 --- a/CoreStore/Saving and Processing/UnsafeDataTransaction.swift +++ b/CoreStore/Saving and Processing/UnsafeDataTransaction.swift @@ -30,10 +30,6 @@ import CoreData #endif -@available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="UnsafeDataTransaction") -public typealias DetachedDataTransaction = UnsafeDataTransaction - - // MARK: - UnsafeDataTransaction /** @@ -131,13 +127,6 @@ public final class UnsafeDataTransaction: BaseDataTransaction { return self.context } - @available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="beginUnsafe") - @warn_unused_result - public func beginDetached() -> UnsafeDataTransaction { - - return self.beginUnsafe() - } - // MARK: Internal @@ -145,4 +134,20 @@ public final class UnsafeDataTransaction: BaseDataTransaction { super.init(mainContext: mainContext, queue: queue, supportsUndo: supportsUndo, bypassesQueueing: true) } + + + // MARK: Deprecated + + @available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="beginUnsafe") + @warn_unused_result + public func beginDetached() -> UnsafeDataTransaction { + + return self.beginUnsafe() + } } + + +// MARK: Deprecated + +@available(*, deprecated=1.3.1, obsoleted=2.0.0, renamed="UnsafeDataTransaction") +public typealias DetachedDataTransaction = UnsafeDataTransaction diff --git a/CoreStore/Setting Up/CoreStore+Setup.swift b/CoreStore/Setting Up/CoreStore+Setup.swift index 97cde84..353f640 100644 --- a/CoreStore/Setting Up/CoreStore+Setup.swift +++ b/CoreStore/Setting Up/CoreStore+Setup.swift @@ -59,14 +59,25 @@ public extension CoreStore { } /** - Adds an in-memory store to the `defaultStack`. + Creates a `Storage` of the specified store type with default values and adds it to the `defaultStack`. This method blocks until completion. - - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`. - - returns: the `NSPersistentStore` added to the stack. + - parameter storeType: the `Storage` type + - returns: the `Storage` added to the `defaultStack` */ - public static func addInMemoryStoreAndWait(configuration configuration: String? = nil) throws -> NSPersistentStore { + public static func addStoreAndWait(storeType: T.Type) throws -> T { - return try self.defaultStack.addInMemoryStoreAndWait(configuration: configuration) + return try self.defaultStack.addStoreAndWait(storeType.init()) + } + + /** + Adds a `Storage` to the `defaultStack` and blocks until completion. + + - parameter store: the `Storage` + - returns: the `Storage` added to the `defaultStack` + */ + public static func addStoreAndWait(store: T) throws -> T { + + return try self.defaultStack.addStoreAndWait(store) } /** @@ -102,4 +113,13 @@ public extension CoreStore { resetStoreOnModelMismatch: resetStoreOnModelMismatch ) } + + + // MARK: Deprecated + + @available(*, deprecated=2.0.0, message="Use addStoreAndWait(_:configuration:) by passing an InMemoryStore instance") + public static func addInMemoryStoreAndWait(configuration configuration: String? = nil) throws -> NSPersistentStore { + + return try self.defaultStack.addInMemoryStoreAndWait(configuration: configuration) + } } diff --git a/CoreStore/Setting Up/DataStack.swift b/CoreStore/Setting Up/DataStack.swift index 07fe3f9..4e8e43f 100644 --- a/CoreStore/Setting Up/DataStack.swift +++ b/CoreStore/Setting Up/DataStack.swift @@ -123,33 +123,33 @@ public final class DataStack { return self.coordinator.managedObjectIDForURIRepresentation(url) } - @available(*, deprecated=2.0.0, message="Use addStoreAndWait(_:configuration:) by passing an InMemoryStore instance") - public func addInMemoryStoreAndWait(configuration configuration: String? = nil) throws -> NSPersistentStore { + /** + Creates a `Storage` of the specified store type with default values and adds it to the stack. This method blocks until completion. + + - parameter storeType: the `Storage` type + - returns: the `Storage` added to the stack + */ + public func addStoreAndWait(storeType: T.Type) throws -> T { - return try self.addStoreAndWait(InMemoryStore()).internalStore! + return try self.addStoreAndWait(storeType.init()) } /** - Adds an in-memory store to the stack. + Adds a `Storage` to the stack and blocks until completion. - - parameter store: the `AtomicStore`. - - returns: the `AtomicStore` added to the stack. + - parameter store: the `Storage` + - returns: the `Storage` added to the stack */ - public func addStoreAndWait(store: T) throws -> T { + public func addStoreAndWait(store: T) throws -> T { CoreStore.assert( store.internalStore == nil, - "The specified store was already added to the data stack:\n\(store)" + "The specified store was already added to the data stack: \(store)" ) do { - let persistentStore = try self.coordinator.addPersistentStoreSynchronously( - T.storeType, - configuration: store.configuration, - URL: store.storeURL, - options: nil - ) + let persistentStore = try store.addToPersistentStoreCoordinatorSynchronously(self.coordinator) self.updateMetadataForPersistentStore(persistentStore) store.internalStore = persistentStore return store @@ -382,4 +382,13 @@ public final class DataStack { let coordinator = self.coordinator coordinator.persistentStores.forEach { _ = try? coordinator.removePersistentStore($0) } } + + + // MARK: Deprecated + + @available(*, deprecated=2.0.0, message="Use addStoreAndWait(_:configuration:) by passing an InMemoryStore instance") + public func addInMemoryStoreAndWait(configuration configuration: String? = nil) throws -> NSPersistentStore { + + return try self.addStoreAndWait(InMemoryStore).internalStore! + } } diff --git a/CoreStore/Setting Up/PersistentStores/InMemoryStore.swift b/CoreStore/Setting Up/PersistentStores/InMemoryStore.swift index cc144c8..1801531 100644 --- a/CoreStore/Setting Up/PersistentStores/InMemoryStore.swift +++ b/CoreStore/Setting Up/PersistentStores/InMemoryStore.swift @@ -28,15 +28,23 @@ import CoreData // MARK: - InMemoryStore -public class InMemoryStore: AtomicStore { +public class InMemoryStore: Storage, DefaultInitializableStore { - public required init(configuration: String? = nil) { + public required init(configuration: String?) { self.configuration = configuration } - // MARK: PersistentStore + // MARK: DefaultInitializableStore + + public required init() { + + self.configuration = nil + } + + + // MARK: Storage public static let storeType = NSInMemoryStoreType @@ -44,5 +52,7 @@ public class InMemoryStore: AtomicStore { public let configuration: String? + public let storeOptions: [String: AnyObject]? = nil + public var internalStore: NSPersistentStore? } diff --git a/CoreStore/Setting Up/PersistentStores/SQLiteStore.swift b/CoreStore/Setting Up/PersistentStores/SQLiteStore.swift new file mode 100644 index 0000000..e7fb4f9 --- /dev/null +++ b/CoreStore/Setting Up/PersistentStores/SQLiteStore.swift @@ -0,0 +1,139 @@ +// +// SQLiteStore.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 CoreData + + +// MARK: - SQLiteStore + +public class SQLiteStore: Storage, DefaultInitializableStore { + + public static let defaultRootDirectory: NSURL = { + + #if os(tvOS) + let systemDirectorySearchPath = NSSearchPathDirectory.CachesDirectory + #else + let systemDirectorySearchPath = NSSearchPathDirectory.ApplicationSupportDirectory + #endif + + let defaultSystemDirectory = NSFileManager + .defaultManager() + .URLsForDirectory(systemDirectorySearchPath, inDomains: .UserDomainMask).first! + + return defaultSystemDirectory.URLByAppendingPathComponent( + NSBundle.mainBundle().bundleIdentifier ?? "com.CoreStore.DataStack", + isDirectory: true + ) + }() + + public static let defaultFileURL: NSURL = { + + let applicationName = (NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData" + + return SQLiteStore.defaultRootDirectory + .URLByAppendingPathComponent(applicationName, isDirectory: false) + .URLByAppendingPathExtension("sqlite") + }() + + public required init(fileURL: NSURL, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) { + + self.fileURL = fileURL + self.configuration = configuration + self.resetStoreOnModelMismatch = resetStoreOnModelMismatch + } + + public required init(fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) { + + self.fileURL = SQLiteStore.defaultRootDirectory + .URLByAppendingPathComponent(fileName, isDirectory: false) + self.configuration = configuration + self.resetStoreOnModelMismatch = resetStoreOnModelMismatch + } + + + // MARK: DefaultInitializableStore + + public required init() { + + self.fileURL = SQLiteStore.defaultFileURL + self.configuration = nil + self.resetStoreOnModelMismatch = false + } + + + // MARK: Storage + + public static let storeType = NSSQLiteStoreType + + public var storeURL: NSURL? { + + return self.fileURL + } + + public let configuration: String? + + public let storeOptions: [String: AnyObject]? = [NSSQLitePragmasOption: ["journal_mode": "WAL"]] + + public var internalStore: NSPersistentStore? + + public func addToPersistentStoreCoordinatorSynchronously(coordinator: NSPersistentStoreCoordinator) throws -> NSPersistentStore { + + let fileManager = NSFileManager.defaultManager() + + do { + + let fileURL = self.fileURL + try fileManager.createDirectoryAtURL( + fileURL.URLByDeletingLastPathComponent!, + withIntermediateDirectories: true, + attributes: nil + ) + return try coordinator.addPersistentStoreSynchronously( + self.dynamicType.storeType, + configuration: self.configuration, + URL: fileURL, + options: self.storeOptions + ) + } + catch let error as NSError where resetStoreOnModelMismatch && error.isCoreDataMigrationError { + + fileManager.removeSQLiteStoreAtURL(fileURL) + + return try coordinator.addPersistentStoreSynchronously( + self.dynamicType.storeType, + configuration: self.configuration, + URL: fileURL, + options: self.storeOptions + ) + } + } + + + // MARK: Internal + + internal let fileURL: NSURL + + internal let resetStoreOnModelMismatch: Bool +} diff --git a/CoreStore/Setting Up/PersistentStores/Storage.swift b/CoreStore/Setting Up/PersistentStores/Storage.swift index f753a50..e812297 100644 --- a/CoreStore/Setting Up/PersistentStores/Storage.swift +++ b/CoreStore/Setting Up/PersistentStores/Storage.swift @@ -23,6 +23,8 @@ // SOFTWARE. // +import CoreData + // MARK: - Storage @@ -34,15 +36,54 @@ public protocol Storage: class { var configuration: String? { get } + var storeOptions: [String: AnyObject]? { get } + var internalStore: NSPersistentStore? { get set } + + func addToPersistentStoreCoordinatorSynchronously(coordinator: NSPersistentStoreCoordinator) throws -> NSPersistentStore + + func addToPersistentStoreCoordinatorAsynchronously(coordinator: NSPersistentStoreCoordinator, completion: (NSPersistentStore) -> Void, failure: (NSError) -> Void) throws +} + +public extension Storage { + + public func addToPersistentStoreCoordinatorSynchronously(coordinator: NSPersistentStoreCoordinator) throws -> NSPersistentStore { + + return try coordinator.addPersistentStoreSynchronously( + self.dynamicType.storeType, + configuration: self.configuration, + URL: self.storeURL, + options: self.storeOptions + ) + } + + public func addToPersistentStoreCoordinatorAsynchronously(coordinator: NSPersistentStoreCoordinator, completion: (NSPersistentStore) -> Void, failure: (NSError) -> Void) throws { + + coordinator.performBlock { + + do { + + let persistentStore = try coordinator.addPersistentStoreWithType( + self.dynamicType.storeType, + configuration: self.configuration, + URL: self.storeURL, + options: self.storeOptions + ) + completion(persistentStore) + } + catch { + + failure(error as NSError) + } + } + } } -// MARK: - AtomicStore +// MARK: - DefaultInitializableStore -public protocol AtomicStore: Storage { } +public protocol DefaultInitializableStore: Storage { + + init() +} - -// MARK: - IncrementalStore - -public protocol IncrementalStore: Storage { }