WIP: documentation

This commit is contained in:
John Rommel Estropia
2016-03-14 07:57:49 +09:00
parent 456977bf12
commit 42a889a28e
9 changed files with 169 additions and 56 deletions

View File

@@ -141,6 +141,10 @@
B52DD1CC1BE1F94D00949AFE /* CoreStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F03A53F19C5C6DA005002A5 /* CoreStoreTests.swift */; }; B52DD1CC1BE1F94D00949AFE /* CoreStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F03A53F19C5C6DA005002A5 /* CoreStoreTests.swift */; };
B52DD1CD1BE1F94D00949AFE /* TestEntity1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D372851A39CDDB00F583D9 /* TestEntity1.swift */; }; B52DD1CD1BE1F94D00949AFE /* TestEntity1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D372851A39CDDB00F583D9 /* TestEntity1.swift */; };
B52DD1CE1BE1F94D00949AFE /* TestEntity2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D5E0CE1A4D6AAB006468AF /* TestEntity2.swift */; }; B52DD1CE1BE1F94D00949AFE /* TestEntity2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D5E0CE1A4D6AAB006468AF /* TestEntity2.swift */; };
B546F9531C95529D00D5AC55 /* LocalStorageOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B546F9521C95529D00D5AC55 /* LocalStorageOptions.swift */; };
B546F9541C95529D00D5AC55 /* LocalStorageOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B546F9521C95529D00D5AC55 /* LocalStorageOptions.swift */; };
B546F9551C95529D00D5AC55 /* LocalStorageOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B546F9521C95529D00D5AC55 /* LocalStorageOptions.swift */; };
B546F9561C95529D00D5AC55 /* LocalStorageOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B546F9521C95529D00D5AC55 /* LocalStorageOptions.swift */; };
B54A6A551BA15F2A007870FD /* FetchedResultsControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */; }; B54A6A551BA15F2A007870FD /* FetchedResultsControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */; };
B5598BCC1BE2093D0092EFCE /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B5D372821A39CD6900F583D9 /* Model.xcdatamodeld */; }; B5598BCC1BE2093D0092EFCE /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B5D372821A39CD6900F583D9 /* Model.xcdatamodeld */; };
B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56007101B3F6BD500A9A8F9 /* Into.swift */; }; B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56007101B3F6BD500A9A8F9 /* Into.swift */; };
@@ -340,6 +344,7 @@
B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFetchedResultsController+Convenience.swift"; sourceTree = "<group>"; }; B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFetchedResultsController+Convenience.swift"; sourceTree = "<group>"; };
B52DD1741BE1F8CC00949AFE /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 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; }; B52DD17D1BE1F8CC00949AFE /* CoreStoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreStoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B546F9521C95529D00D5AC55 /* LocalStorageOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalStorageOptions.swift; sourceTree = "<group>"; };
B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchedResultsControllerDelegate.swift; sourceTree = "<group>"; }; B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchedResultsControllerDelegate.swift; sourceTree = "<group>"; };
B5548CD51BD65AE00077652A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; B5548CD51BD65AE00077652A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
B5548CD71BD65AE50077652A /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; }; B5548CD71BD65AE50077652A /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; };
@@ -734,6 +739,7 @@
B5FE4DA01C84818B00FA6A91 /* StorageInterfaces */ = { B5FE4DA01C84818B00FA6A91 /* StorageInterfaces */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B546F9521C95529D00D5AC55 /* LocalStorageOptions.swift */,
B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */, B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */,
B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */, B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */,
B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */, B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */,
@@ -1058,6 +1064,7 @@
B5FEC18E1C9166E200532541 /* NSPersistentStore+Setup.swift in Sources */, B5FEC18E1C9166E200532541 /* NSPersistentStore+Setup.swift in Sources */,
B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */, B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */,
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
B546F9531C95529D00D5AC55 /* LocalStorageOptions.swift in Sources */,
B5E834BB1B7691F3001D3D50 /* Functions.swift in Sources */, B5E834BB1B7691F3001D3D50 /* Functions.swift in Sources */,
B5E84F231AFF84860064E85B /* ListMonitor.swift in Sources */, B5E84F231AFF84860064E85B /* ListMonitor.swift in Sources */,
B5E84EF71AFF846E0064E85B /* UnsafeDataTransaction.swift in Sources */, B5E84EF71AFF846E0064E85B /* UnsafeDataTransaction.swift in Sources */,
@@ -1143,6 +1150,7 @@
B5FEC18F1C9166E600532541 /* NSPersistentStore+Setup.swift in Sources */, B5FEC18F1C9166E600532541 /* NSPersistentStore+Setup.swift in Sources */,
82BA18B71C4BBD3F00A0916E /* CoreStore+Querying.swift in Sources */, 82BA18B71C4BBD3F00A0916E /* CoreStore+Querying.swift in Sources */,
82BA18A41C4BBD2200A0916E /* SetupResult.swift in Sources */, 82BA18A41C4BBD2200A0916E /* SetupResult.swift in Sources */,
B546F9541C95529D00D5AC55 /* LocalStorageOptions.swift in Sources */,
82BA18AA1C4BBD3100A0916E /* BaseDataTransaction.swift in Sources */, 82BA18AA1C4BBD3100A0916E /* BaseDataTransaction.swift in Sources */,
82BA18A91C4BBD3100A0916E /* Into.swift in Sources */, 82BA18A91C4BBD3100A0916E /* Into.swift in Sources */,
82BA18D11C4BBD7100A0916E /* NotificationObserver.swift in Sources */, 82BA18D11C4BBD7100A0916E /* NotificationObserver.swift in Sources */,
@@ -1217,6 +1225,7 @@
B52DD1BC1BE1F94000949AFE /* MigrationResult.swift in Sources */, B52DD1BC1BE1F94000949AFE /* MigrationResult.swift in Sources */,
B52DD19D1BE1F92C00949AFE /* BaseDataTransaction.swift in Sources */, B52DD19D1BE1F92C00949AFE /* BaseDataTransaction.swift in Sources */,
B52DD1B81BE1F94000949AFE /* DataStack+Migration.swift in Sources */, B52DD1B81BE1F94000949AFE /* DataStack+Migration.swift in Sources */,
B546F9561C95529D00D5AC55 /* LocalStorageOptions.swift in Sources */,
B52DD1A51BE1F92F00949AFE /* ImportableUniqueObject.swift in Sources */, B52DD1A51BE1F92F00949AFE /* ImportableUniqueObject.swift in Sources */,
B52DD19C1BE1F92C00949AFE /* Into.swift in Sources */, B52DD19C1BE1F92C00949AFE /* Into.swift in Sources */,
B5FE4DA51C8481E100FA6A91 /* StorageInterface.swift in Sources */, B5FE4DA51C8481E100FA6A91 /* StorageInterface.swift in Sources */,
@@ -1302,6 +1311,7 @@
B5FEC1901C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */, B5FEC1901C9166E700532541 /* NSPersistentStore+Setup.swift in Sources */,
B56321A11BD65216006C9394 /* ListMonitor.swift in Sources */, B56321A11BD65216006C9394 /* ListMonitor.swift in Sources */,
B56321881BD65216006C9394 /* BaseDataTransaction.swift in Sources */, B56321881BD65216006C9394 /* BaseDataTransaction.swift in Sources */,
B546F9551C95529D00D5AC55 /* LocalStorageOptions.swift in Sources */,
B56321A31BD65216006C9394 /* DataStack+Migration.swift in Sources */, B56321A31BD65216006C9394 /* DataStack+Migration.swift in Sources */,
B56321901BD65216006C9394 /* ImportableUniqueObject.swift in Sources */, B56321901BD65216006C9394 /* ImportableUniqueObject.swift in Sources */,
B56321871BD65216006C9394 /* Into.swift in Sources */, B56321871BD65216006C9394 /* Into.swift in Sources */,

View File

@@ -54,7 +54,7 @@ class CoreStoreTests: XCTestCase {
SQLiteStore( SQLiteStore(
fileName: "ConfigStore1.sqlite", fileName: "ConfigStore1.sqlite",
configuration: "Config1", configuration: "Config1",
resetStoreOnModelMismatch: true localStorageOptions: .RecreateStoreOnModelMismatch
) )
) )
} }
@@ -69,7 +69,7 @@ class CoreStoreTests: XCTestCase {
SQLiteStore( SQLiteStore(
fileName: "ConfigStore2.sqlite", fileName: "ConfigStore2.sqlite",
configuration: "Config2", configuration: "Config2",
resetStoreOnModelMismatch: true localStorageOptions: .RecreateStoreOnModelMismatch
) )
) )
} }

View File

@@ -81,7 +81,7 @@ class StorageInterfaceTests: XCTestCase {
XCTAssertEqual(store.fileURL, SQLiteStore.defaultFileURL) XCTAssertEqual(store.fileURL, SQLiteStore.defaultFileURL)
XCTAssertEqual(store.mappingModelBundles, NSBundle.allBundles()) XCTAssertEqual(store.mappingModelBundles, NSBundle.allBundles())
XCTAssertFalse(store.resetStoreOnModelMismatch) XCTAssertEqual(store.localStorageOptions, [.None])
} }
func testSQLiteStoreFileURL() { func testSQLiteStoreFileURL() {
@@ -95,7 +95,7 @@ class StorageInterfaceTests: XCTestCase {
fileURL: fileURL, fileURL: fileURL,
configuration: "config1", configuration: "config1",
mappingModelBundles: bundles, mappingModelBundles: bundles,
resetStoreOnModelMismatch: true localStorageOptions: .RecreateStoreOnModelMismatch
) )
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType) XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
XCTAssertEqual(store.configuration, "config1") XCTAssertEqual(store.configuration, "config1")
@@ -103,7 +103,7 @@ class StorageInterfaceTests: XCTestCase {
XCTAssertEqual(store.fileURL, fileURL) XCTAssertEqual(store.fileURL, fileURL)
XCTAssertEqual(store.mappingModelBundles, bundles) XCTAssertEqual(store.mappingModelBundles, bundles)
XCTAssertTrue(store.resetStoreOnModelMismatch) XCTAssertEqual(store.localStorageOptions, [.RecreateStoreOnModelMismatch])
} }
func testSQLiteStoreFileName() { func testSQLiteStoreFileName() {
@@ -115,7 +115,7 @@ class StorageInterfaceTests: XCTestCase {
fileName: fileName, fileName: fileName,
configuration: "config1", configuration: "config1",
mappingModelBundles: bundles, mappingModelBundles: bundles,
resetStoreOnModelMismatch: true localStorageOptions: .RecreateStoreOnModelMismatch
) )
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType) XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
XCTAssertEqual(store.configuration, "config1") XCTAssertEqual(store.configuration, "config1")
@@ -124,7 +124,7 @@ class StorageInterfaceTests: XCTestCase {
XCTAssertEqual(store.fileURL.URLByDeletingLastPathComponent, SQLiteStore.defaultRootDirectory) XCTAssertEqual(store.fileURL.URLByDeletingLastPathComponent, SQLiteStore.defaultRootDirectory)
XCTAssertEqual(store.fileURL.lastPathComponent, fileName) XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
XCTAssertEqual(store.mappingModelBundles, bundles) XCTAssertEqual(store.mappingModelBundles, bundles)
XCTAssertTrue(store.resetStoreOnModelMismatch) XCTAssertEqual(store.localStorageOptions, [.RecreateStoreOnModelMismatch])
} }
func testLegacySQLiteStoreDefaultDirectories() { func testLegacySQLiteStoreDefaultDirectories() {
@@ -157,7 +157,7 @@ class StorageInterfaceTests: XCTestCase {
XCTAssertEqual(store.fileURL, LegacySQLiteStore.defaultFileURL) XCTAssertEqual(store.fileURL, LegacySQLiteStore.defaultFileURL)
XCTAssertEqual(store.mappingModelBundles, NSBundle.allBundles()) XCTAssertEqual(store.mappingModelBundles, NSBundle.allBundles())
XCTAssertFalse(store.resetStoreOnModelMismatch) XCTAssertEqual(store.localStorageOptions, [.None])
} }
func testLegacySQLiteStoreFileURL() { func testLegacySQLiteStoreFileURL() {
@@ -171,7 +171,7 @@ class StorageInterfaceTests: XCTestCase {
fileURL: fileURL, fileURL: fileURL,
configuration: "config1", configuration: "config1",
mappingModelBundles: bundles, mappingModelBundles: bundles,
resetStoreOnModelMismatch: true localStorageOptions: .RecreateStoreOnModelMismatch
) )
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType) XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
XCTAssertEqual(store.configuration, "config1") XCTAssertEqual(store.configuration, "config1")
@@ -179,7 +179,7 @@ class StorageInterfaceTests: XCTestCase {
XCTAssertEqual(store.fileURL, fileURL) XCTAssertEqual(store.fileURL, fileURL)
XCTAssertEqual(store.mappingModelBundles, bundles) XCTAssertEqual(store.mappingModelBundles, bundles)
XCTAssertTrue(store.resetStoreOnModelMismatch) XCTAssertEqual(store.localStorageOptions, [.RecreateStoreOnModelMismatch])
} }
func testLegacySQLiteStoreFileName() { func testLegacySQLiteStoreFileName() {
@@ -191,7 +191,7 @@ class StorageInterfaceTests: XCTestCase {
fileName: fileName, fileName: fileName,
configuration: "config1", configuration: "config1",
mappingModelBundles: bundles, mappingModelBundles: bundles,
resetStoreOnModelMismatch: true localStorageOptions: .RecreateStoreOnModelMismatch
) )
XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType) XCTAssertEqual(store.dynamicType.storeType, NSSQLiteStoreType)
XCTAssertEqual(store.configuration, "config1") XCTAssertEqual(store.configuration, "config1")
@@ -200,6 +200,6 @@ class StorageInterfaceTests: XCTestCase {
XCTAssertEqual(store.fileURL.URLByDeletingLastPathComponent, LegacySQLiteStore.defaultRootDirectory) XCTAssertEqual(store.fileURL.URLByDeletingLastPathComponent, LegacySQLiteStore.defaultRootDirectory)
XCTAssertEqual(store.fileURL.lastPathComponent, fileName) XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
XCTAssertEqual(store.mappingModelBundles, bundles) XCTAssertEqual(store.mappingModelBundles, bundles)
XCTAssertTrue(store.resetStoreOnModelMismatch) XCTAssertEqual(store.localStorageOptions, [.RecreateStoreOnModelMismatch])
} }
} }

View File

@@ -54,7 +54,11 @@ public extension DataStack {
do { do {
try self.createPersistentStoreFromStorage(storage, finalURL: nil) try self.createPersistentStoreFromStorage(
storage,
finalURL: nil,
finalStoreOptions: storage.storeOptions
)
GCDQueue.Main.async { GCDQueue.Main.async {
@@ -85,13 +89,20 @@ 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`.) Asynchronously adds a `LocalStorage` to the stack.
```
- 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 "<Application name>.sqlite" file in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them. try dataStack.addStorage(
- 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. SQLiteStore(configuration: "Config1"),
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. completion: { result in
- 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. switch result {
- 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. case .Success(let storage): // ...
case .Failure(let error): // ...
}
}
)
```
- parameter storage: the local storage
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `SetupResult` 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. Note that the `LocalStorage` associated to the `SetupResult.Success` 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.
- returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required - returns: an `NSProgress` instance if a migration has started, or `nil` is no migrations are required
*/ */
public func addStorage<T: LocalStorage>(storage: T, completion: (SetupResult<T>) -> Void) throws -> NSProgress? { public func addStorage<T: LocalStorage>(storage: T, completion: (SetupResult<T>) -> Void) throws -> NSProgress? {
@@ -154,7 +165,7 @@ public extension DataStack {
if case .Failure(let error) = result { if case .Failure(let error) = result {
if storage.resetStoreOnModelMismatch && error.isCoreDataMigrationError { if storage.localStorageOptions.contains(.RecreateStoreOnModelMismatch) && error.isCoreDataMigrationError {
do { do {
@@ -283,7 +294,7 @@ public extension DataStack {
options: storage.storeOptions options: storage.storeOptions
) )
guard let migrationSteps = self.computeMigrationFromStorageMetadata(metadata, configuration: storage.configuration, mappingModelBundles: storage.mappingModelBundles) else { guard let migrationSteps = self.computeMigrationFromStorage(storage, metadata: metadata) else {
let error = NSError(coreStoreErrorCode: .MappingModelNotFound) let error = NSError(coreStoreErrorCode: .MappingModelNotFound)
CoreStore.handleError( CoreStore.handleError(
@@ -316,7 +327,7 @@ public extension DataStack {
private func upgradeStorageIfNeeded<T: LocalStorage>(storage: T, metadata: [String: AnyObject], completion: (MigrationResult) -> Void) -> NSProgress? { private func upgradeStorageIfNeeded<T: LocalStorage>(storage: T, metadata: [String: AnyObject], completion: (MigrationResult) -> Void) -> NSProgress? {
guard let migrationSteps = self.computeMigrationFromStorageMetadata(metadata, configuration: storage.configuration, mappingModelBundles: storage.mappingModelBundles) else { guard let migrationSteps = self.computeMigrationFromStorage(storage, metadata: metadata) else {
CoreStore.handleError( CoreStore.handleError(
NSError(coreStoreErrorCode: .MappingModelNotFound), NSError(coreStoreErrorCode: .MappingModelNotFound),
@@ -413,10 +424,10 @@ public extension DataStack {
return progress return progress
} }
private func computeMigrationFromStorageMetadata(metadata: [String: AnyObject], configuration: String?, mappingModelBundles: [NSBundle]) -> [(sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, migrationType: MigrationType)]? { private func computeMigrationFromStorage<T: LocalStorage>(storage: T, metadata: [String: AnyObject]) -> [(sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, migrationType: MigrationType)]? {
let model = self.model let model = self.model
if model.isConfiguration(configuration, compatibleWithStoreMetadata: metadata) { if model.isConfiguration(storage.configuration, compatibleWithStoreMetadata: metadata) {
return [] return []
} }
@@ -438,7 +449,7 @@ public extension DataStack {
let destinationModel = model[nextVersion] where sourceModel != model { let destinationModel = model[nextVersion] where sourceModel != model {
if let mappingModel = NSMappingModel( if let mappingModel = NSMappingModel(
fromBundles: mappingModelBundles, fromBundles: storage.mappingModelBundles,
forSourceModel: sourceModel, forSourceModel: sourceModel,
destinationModel: destinationModel) { destinationModel: destinationModel) {
@@ -623,7 +634,7 @@ public extension DataStack {
fileName: fileName, fileName: fileName,
configuration: configuration, configuration: configuration,
mappingModelBundles: mappingModelBundles ?? NSBundle.allBundles(), mappingModelBundles: mappingModelBundles ?? NSBundle.allBundles(),
resetStoreOnModelMismatch: resetStoreOnModelMismatch localStorageOptions: resetStoreOnModelMismatch ? .RecreateStoreOnModelMismatch : .None
), ),
completion: { result in completion: { result in
@@ -652,7 +663,7 @@ public extension DataStack {
fileURL: fileURL, fileURL: fileURL,
configuration: configuration, configuration: configuration,
mappingModelBundles: mappingModelBundles ?? NSBundle.allBundles(), mappingModelBundles: mappingModelBundles ?? NSBundle.allBundles(),
resetStoreOnModelMismatch: resetStoreOnModelMismatch localStorageOptions: resetStoreOnModelMismatch ? .RecreateStoreOnModelMismatch : .None
), ),
completion: { result in completion: { result in

View File

@@ -158,7 +158,11 @@ public final class DataStack {
return storage return storage
} }
try self.createPersistentStoreFromStorage(storage, finalURL: nil) try self.createPersistentStoreFromStorage(
storage,
finalURL: nil,
finalStoreOptions: storage.storeOptions
)
return storage return storage
} }
} }
@@ -228,6 +232,12 @@ public final class DataStack {
do { do {
var storeOptions = storage.storeOptions ?? [:]
if storage.localStorageOptions.contains(.AllowSynchronousLightweightMigration) {
storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true
storeOptions[NSInferMappingModelAutomaticallyOption] = true
}
do { do {
try NSFileManager.defaultManager().createDirectoryAtURL( try NSFileManager.defaultManager().createDirectoryAtURL(
@@ -235,19 +245,27 @@ public final class DataStack {
withIntermediateDirectories: true, withIntermediateDirectories: true,
attributes: nil attributes: nil
) )
try self.createPersistentStoreFromStorage(storage, finalURL: fileURL) try self.createPersistentStoreFromStorage(
storage,
finalURL: fileURL,
finalStoreOptions: storeOptions
)
return storage return storage
} }
catch let error as NSError where storage.resetStoreOnModelMismatch && error.isCoreDataMigrationError { catch let error as NSError where storage.localStorageOptions.contains(.RecreateStoreOnModelMismatch) && error.isCoreDataMigrationError {
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
storage.dynamicType.storeType, storage.dynamicType.storeType,
URL: fileURL, URL: fileURL,
options: storage.storeOptions options: storeOptions
) )
try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait) try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait)
try self.createPersistentStoreFromStorage(storage, finalURL: fileURL) try self.createPersistentStoreFromStorage(
storage,
finalURL: fileURL,
finalStoreOptions: storeOptions
)
return storage return storage
} }
} }
@@ -343,13 +361,13 @@ public final class DataStack {
return returnValue return returnValue
} }
internal func createPersistentStoreFromStorage(storage: StorageInterface, finalURL: NSURL?) throws -> NSPersistentStore { internal func createPersistentStoreFromStorage(storage: StorageInterface, finalURL: NSURL?, finalStoreOptions: [String: AnyObject]?) throws -> NSPersistentStore {
let persistentStore = try self.coordinator.addPersistentStoreWithType( let persistentStore = try self.coordinator.addPersistentStoreWithType(
storage.dynamicType.storeType, storage.dynamicType.storeType,
configuration: storage.configuration, configuration: storage.configuration,
URL: finalURL, URL: finalURL,
options: storage.storeOptions options: finalStoreOptions
) )
persistentStore.storageInterface = storage persistentStore.storageInterface = storage
@@ -412,7 +430,7 @@ public final class DataStack {
LegacySQLiteStore( LegacySQLiteStore(
fileName: fileName, fileName: fileName,
configuration: configuration, configuration: configuration,
resetStoreOnModelMismatch: resetStoreOnModelMismatch localStorageOptions: resetStoreOnModelMismatch ? .RecreateStoreOnModelMismatch : .None
) )
) )
return self.persistentStoreForStorage(storage)! return self.persistentStoreForStorage(storage)!
@@ -430,7 +448,7 @@ public final class DataStack {
LegacySQLiteStore( LegacySQLiteStore(
fileURL: fileURL, fileURL: fileURL,
configuration: configuration, configuration: configuration,
resetStoreOnModelMismatch: resetStoreOnModelMismatch localStorageOptions: resetStoreOnModelMismatch ? .RecreateStoreOnModelMismatch : .None
) )
) )
return self.persistentStoreForStorage(storage)! return self.persistentStoreForStorage(storage)!

View File

@@ -40,15 +40,15 @@ public final class LegacySQLiteStore: SQLiteStore {
- parameter fileURL: the local file URL for the target SQLite persistent store. 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 target SQLite persistent store. 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 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: a list of `NSBundle`s from which to search mapping models for migration. - parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration.
- parameter resetStoreOnModelMismatch: When the `LegacySQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, a `true` value tells the `DataStack` to delete the store on model mismatch; a `false` value lets exceptions be thrown on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to `false`. - parameter localStorageOptions: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
*/ */
public required init(fileURL: NSURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false) { public required init(fileURL: NSURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles(), localStorageOptions: LocalStorageOptions = nil) {
super.init( super.init(
fileURL: fileURL, fileURL: fileURL,
configuration: configuration, configuration: configuration,
mappingModelBundles: mappingModelBundles, mappingModelBundles: mappingModelBundles,
resetStoreOnModelMismatch: resetStoreOnModelMismatch localStorageOptions: localStorageOptions
) )
} }
@@ -59,9 +59,9 @@ public final class LegacySQLiteStore: SQLiteStore {
- parameter fileName: the local filename for the SQLite persistent store 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 `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). 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 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: a list of `NSBundle`s from which to search mapping models for migration. - parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration.
- parameter resetStoreOnModelMismatch: When the `LegacySQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, a true value tells the `DataStack` to delete the store on model mismatch; a false value lets exceptions be thrown on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - parameter localStorageOptions: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
*/ */
public required init(fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false) { public required init(fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles(), localStorageOptions: LocalStorageOptions = nil) {
super.init( super.init(
fileURL: LegacySQLiteStore.defaultRootDirectory.URLByAppendingPathComponent( fileURL: LegacySQLiteStore.defaultRootDirectory.URLByAppendingPathComponent(
@@ -70,7 +70,7 @@ public final class LegacySQLiteStore: SQLiteStore {
), ),
configuration: configuration, configuration: configuration,
mappingModelBundles: mappingModelBundles, mappingModelBundles: mappingModelBundles,
resetStoreOnModelMismatch: resetStoreOnModelMismatch localStorageOptions: localStorageOptions
) )
} }
@@ -102,7 +102,7 @@ public final class LegacySQLiteStore: SQLiteStore {
// MARK: DefaultInitializableStore // MARK: DefaultInitializableStore
/** /**
Initializes an `LegacySQLiteStore` with an all-default settings: a `fileURL` pointing to a "<Application name>.sqlite" file in the "Application Support" directory (or the "Caches" directory on tvOS), a `nil` `configuration` pertaining to the "Default" configuration, a `mappingModelBundles` set to search all `NSBundle`s, and `resetStoreOnModelMismatch` disabled. Initializes an `LegacySQLiteStore` with an all-default settings: a `fileURL` pointing to a "<Application name>.sqlite" file in the "Application Support" directory (or the "Caches" directory on tvOS), a `nil` `configuration` pertaining to the "Default" configuration, a `mappingModelBundles` set to search all `NSBundle`s, and `localStorageOptions` set to `.AllowProgresiveMigration`.
- Warning: The default SQLite file location for the `LegacySQLiteStore` and `SQLiteStore` are different. If the app was depending on CoreStore's default directories prior to 2.0.0, make sure to use `LegacySQLiteStore` instead of `SQLiteStore`. - Warning: The default SQLite file location for the `LegacySQLiteStore` and `SQLiteStore` are different. If the app was depending on CoreStore's default directories prior to 2.0.0, make sure to use `LegacySQLiteStore` instead of `SQLiteStore`.
*/ */
public required init() { public required init() {

View File

@@ -0,0 +1,74 @@
//
// LocalStorageOptions.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.
//
// MARK: - LocalStorageOptions
/**
The `LocalStorageOptions` provides settings that tells the `DataStack` how to setup the persistent store for `LocalStorage` implementers.
*/
public struct LocalStorageOptions: OptionSetType, NilLiteralConvertible {
/**
Tells the `DataStack` that the store should not be migrated or recreated, and should simply fail on model mismatch
*/
public static let None = LocalStorageOptions(rawValue: 0)
/**
Tells the `DataStack` to delete and recreate the store on model mismatch, otherwise exceptions will be thrown on failure instead
*/
public static let RecreateStoreOnModelMismatch = LocalStorageOptions(rawValue: 1 << 0)
/**
Tells the `DataStack` to prevent progressive migrations for the store
*/
public static let PreventProgressiveMigration = LocalStorageOptions(rawValue: 1 << 1)
/**
Tells the `DataStack` to allow lightweight migration for the store when added synchronously
*/
public static let AllowSynchronousLightweightMigration = LocalStorageOptions(rawValue: 1 << 2)
// MARK: OptionSetType
public init(rawValue: Int) {
self.rawValue = rawValue
}
// MARK: RawRepresentable
public let rawValue: Int
// MARK: NilLiteralConvertible
public init(nilLiteral: ()) {
self.rawValue = 0
}
}

View File

@@ -40,14 +40,14 @@ public class SQLiteStore: LocalStorage, DefaultInitializableStore {
- parameter fileURL: the local file URL for the target SQLite persistent store. 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 target SQLite persistent store. 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 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: a list of `NSBundle`s from which to search mapping models for migration. - parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration.
- parameter resetStoreOnModelMismatch: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, a `true` value tells the `DataStack` to delete the store on model mismatch; a `false` value lets exceptions be thrown on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to `false`. - parameter localStorageOptions: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
*/ */
public required init(fileURL: NSURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false) { public required init(fileURL: NSURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles(), localStorageOptions: LocalStorageOptions = nil) {
self.fileURL = fileURL self.fileURL = fileURL
self.configuration = configuration self.configuration = configuration
self.mappingModelBundles = mappingModelBundles self.mappingModelBundles = mappingModelBundles
self.resetStoreOnModelMismatch = resetStoreOnModelMismatch self.localStorageOptions = localStorageOptions
} }
/** /**
@@ -57,22 +57,22 @@ public class SQLiteStore: LocalStorage, DefaultInitializableStore {
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). 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/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). 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 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: a list of `NSBundle`s from which to search mapping models for migration - parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration
- parameter resetStoreOnModelMismatch: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, a `true` value tells the `DataStack` to delete the store on model mismatch; a `false` value lets exceptions be thrown on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to `false`. - parameter localStorageOptions: When the `SQLiteStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
*/ */
public required init(fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles(), resetStoreOnModelMismatch: Bool = false) { public required init(fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles(), localStorageOptions: LocalStorageOptions = nil) {
self.fileURL = SQLiteStore.defaultRootDirectory self.fileURL = SQLiteStore.defaultRootDirectory
.URLByAppendingPathComponent(fileName, isDirectory: false) .URLByAppendingPathComponent(fileName, isDirectory: false)
self.configuration = configuration self.configuration = configuration
self.mappingModelBundles = mappingModelBundles self.mappingModelBundles = mappingModelBundles
self.resetStoreOnModelMismatch = resetStoreOnModelMismatch self.localStorageOptions = localStorageOptions
} }
// MARK: DefaultInitializableStore // MARK: DefaultInitializableStore
/** /**
Initializes an `SQLiteStore` with an all-default settings: a `fileURL` pointing to a "<Application name>.sqlite" file in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS), a `nil` `configuration` pertaining to the "Default" configuration, a `mappingModelBundles` set to search all `NSBundle`s, and `resetStoreOnModelMismatch` disabled. Initializes an `SQLiteStore` with an all-default settings: a `fileURL` pointing to a "<Application name>.sqlite" file in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS), a `nil` `configuration` pertaining to the "Default" configuration, a `mappingModelBundles` set to search all `NSBundle`s, and `localStorageOptions` set to `.AllowProgresiveMigration`.
- Warning: The default SQLite file location for the `LegacySQLiteStore` and `SQLiteStore` are different. If the app was depending on CoreStore's default directories prior to 2.0.0, make sure to use `LegacySQLiteStore` instead of `SQLiteStore`. - Warning: The default SQLite file location for the `LegacySQLiteStore` and `SQLiteStore` are different. If the app was depending on CoreStore's default directories prior to 2.0.0, make sure to use `LegacySQLiteStore` instead of `SQLiteStore`.
*/ */
public required init() { public required init() {
@@ -80,7 +80,7 @@ public class SQLiteStore: LocalStorage, DefaultInitializableStore {
self.fileURL = SQLiteStore.defaultFileURL self.fileURL = SQLiteStore.defaultFileURL
self.configuration = nil self.configuration = nil
self.mappingModelBundles = NSBundle.allBundles() self.mappingModelBundles = NSBundle.allBundles()
self.resetStoreOnModelMismatch = false self.localStorageOptions = nil
} }
@@ -97,9 +97,9 @@ public class SQLiteStore: LocalStorage, DefaultInitializableStore {
public let mappingModelBundles: [NSBundle] public let mappingModelBundles: [NSBundle]
/** /**
When `true`, tells the `DataStack` to delete and recreate the store on model mismatch, otherwise exceptions will be thrown on failure instead. Options that tell the `DataStack` how to setup the persistent store
*/ */
public let resetStoreOnModelMismatch: Bool public var localStorageOptions: LocalStorageOptions
// MARK: StorageInterface // MARK: StorageInterface

View File

@@ -82,9 +82,9 @@ public protocol LocalStorage: StorageInterface {
var mappingModelBundles: [NSBundle] { get } var mappingModelBundles: [NSBundle] { get }
/** /**
When `true`, tells the `DataStack` to delete and recreate the store on model mismatch, otherwise exceptions will be thrown on failure instead. Options that tell the `DataStack` how to setup the persistent store
*/ */
var resetStoreOnModelMismatch: Bool { get } var localStorageOptions: LocalStorageOptions { get }
/** /**
Called by the `DataStack` to perform actual deletion of the store file from disk. Do not call directly! The `sourceModel` argument is a hint for the existing store's model version. Implementers can use the `sourceModel` to perform necessary store operations. (SQLite stores for example, can convert WAL journaling mode to DELETE before deleting) Called by the `DataStack` to perform actual deletion of the store file from disk. Do not call directly! The `sourceModel` argument is a hint for the existing store's model version. Implementers can use the `sourceModel` to perform necessary store operations. (SQLite stores for example, can convert WAL journaling mode to DELETE before deleting)