mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-31 06:33:06 +02:00
WIP: ICloudStore prototype
This commit is contained in:
@@ -290,6 +290,11 @@
|
|||||||
B56321B41BD6521C006C9394 /* NSManagedObjectContext+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */; };
|
B56321B41BD6521C006C9394 /* NSManagedObjectContext+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */; };
|
||||||
B56321B51BD6521C006C9394 /* NSManagedObjectModel+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51BE0691B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift */; };
|
B56321B51BD6521C006C9394 /* NSManagedObjectModel+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B51BE0691B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift */; };
|
||||||
B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F2D1AFF849C0064E85B /* WeakObject.swift */; };
|
B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84F2D1AFF849C0064E85B /* WeakObject.swift */; };
|
||||||
|
B5677D3D1CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5677D3C1CD3B1E400322BFC /* ICloudStoreObserver.swift */; };
|
||||||
|
B5677D3E1CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5677D3C1CD3B1E400322BFC /* ICloudStoreObserver.swift */; };
|
||||||
|
B5677D3F1CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5677D3C1CD3B1E400322BFC /* ICloudStoreObserver.swift */; };
|
||||||
|
B5677D401CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5677D3C1CD3B1E400322BFC /* ICloudStoreObserver.swift */; };
|
||||||
|
B5677D411CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5677D3C1CD3B1E400322BFC /* ICloudStoreObserver.swift */; };
|
||||||
B56964D41B22FFAD0075EE4A /* DataStack+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56964D31B22FFAD0075EE4A /* DataStack+Migration.swift */; };
|
B56964D41B22FFAD0075EE4A /* DataStack+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56964D31B22FFAD0075EE4A /* DataStack+Migration.swift */; };
|
||||||
B56965241B356B820075EE4A /* MigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56965231B356B820075EE4A /* MigrationResult.swift */; };
|
B56965241B356B820075EE4A /* MigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56965231B356B820075EE4A /* MigrationResult.swift */; };
|
||||||
B58B22F51C93C1BA00521925 /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F03A53019C5C6DA005002A5 /* CoreStore.framework */; };
|
B58B22F51C93C1BA00521925 /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F03A53019C5C6DA005002A5 /* CoreStore.framework */; };
|
||||||
@@ -298,6 +303,11 @@
|
|||||||
B598514B1C90289F00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */; };
|
B598514B1C90289F00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */; };
|
||||||
B59983491CA54BC100E1A417 /* CSBaseDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A581CA2008C002BEF78 /* CSBaseDataTransaction.swift */; };
|
B59983491CA54BC100E1A417 /* CSBaseDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A581CA2008C002BEF78 /* CSBaseDataTransaction.swift */; };
|
||||||
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */; };
|
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */; };
|
||||||
|
B59FA0AE1CCBAC95007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; };
|
||||||
|
B59FA0AF1CCBACA6007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; };
|
||||||
|
B59FA0B01CCBACA7007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; };
|
||||||
|
B59FA0B11CCBACA7007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; };
|
||||||
|
B59FA0B21CCBACA8007C9BCA /* ICloudStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */; };
|
||||||
B5A261211B64BFDB006EB6D3 /* MigrationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A261201B64BFDB006EB6D3 /* MigrationType.swift */; };
|
B5A261211B64BFDB006EB6D3 /* MigrationType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A261201B64BFDB006EB6D3 /* MigrationType.swift */; };
|
||||||
B5A5F2661CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; };
|
B5A5F2661CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; };
|
||||||
B5A5F2671CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; };
|
B5A5F2671CAEC50F004AB9AF /* CSSelect.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */; };
|
||||||
@@ -652,9 +662,11 @@
|
|||||||
B563216F1BD65082006C9394 /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
B563216F1BD65082006C9394 /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
B56321791BD650DE006C9394 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; };
|
B56321791BD650DE006C9394 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
B563217B1BD650E3006C9394 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
|
B563217B1BD650E3006C9394 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS2.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
|
B5677D3C1CD3B1E400322BFC /* ICloudStoreObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ICloudStoreObserver.swift; sourceTree = "<group>"; };
|
||||||
B56964D31B22FFAD0075EE4A /* DataStack+Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Migration.swift"; sourceTree = "<group>"; };
|
B56964D31B22FFAD0075EE4A /* DataStack+Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Migration.swift"; sourceTree = "<group>"; };
|
||||||
B56965231B356B820075EE4A /* MigrationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationResult.swift; sourceTree = "<group>"; };
|
B56965231B356B820075EE4A /* MigrationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationResult.swift; sourceTree = "<group>"; };
|
||||||
B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStoreCoordinator+Setup.swift"; sourceTree = "<group>"; };
|
B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStoreCoordinator+Setup.swift"; sourceTree = "<group>"; };
|
||||||
|
B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ICloudStore.swift; sourceTree = "<group>"; };
|
||||||
B5A261201B64BFDB006EB6D3 /* MigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationType.swift; sourceTree = "<group>"; };
|
B5A261201B64BFDB006EB6D3 /* MigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationType.swift; sourceTree = "<group>"; };
|
||||||
B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSelect.swift; sourceTree = "<group>"; };
|
B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSelect.swift; sourceTree = "<group>"; };
|
||||||
B5AD60CD1C90141E00F2B2E8 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = SOURCE_ROOT; };
|
B5AD60CD1C90141E00F2B2E8 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = SOURCE_ROOT; };
|
||||||
@@ -1228,6 +1240,8 @@
|
|||||||
B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */,
|
B5FE4DA61C84FB4400FA6A91 /* InMemoryStore.swift */,
|
||||||
B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */,
|
B5FE4DAB1C85D44E00FA6A91 /* SQLiteStore.swift */,
|
||||||
B5D3F6441C887C0A00C7492A /* LegacySQLiteStore.swift */,
|
B5D3F6441C887C0A00C7492A /* LegacySQLiteStore.swift */,
|
||||||
|
B59FA0AD1CCBAC95007C9BCA /* ICloudStore.swift */,
|
||||||
|
B5677D3C1CD3B1E400322BFC /* ICloudStoreObserver.swift */,
|
||||||
);
|
);
|
||||||
path = StorageInterfaces;
|
path = StorageInterfaces;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1576,6 +1590,7 @@
|
|||||||
B5ECDC291CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
B5ECDC291CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
||||||
B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */,
|
B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */,
|
||||||
B546F9581C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
B546F9581C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
||||||
|
B5677D3D1CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */,
|
||||||
B5E84F361AFF85470064E85B /* NSManagedObjectContext+Setup.swift in Sources */,
|
B5E84F361AFF85470064E85B /* NSManagedObjectContext+Setup.swift in Sources */,
|
||||||
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */,
|
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */,
|
||||||
B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */,
|
B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */,
|
||||||
@@ -1633,6 +1648,7 @@
|
|||||||
B5E84F101AFF847B0064E85B /* GroupBy.swift in Sources */,
|
B5E84F101AFF847B0064E85B /* GroupBy.swift in Sources */,
|
||||||
B5E84F201AFF84860064E85B /* DataStack+Observing.swift in Sources */,
|
B5E84F201AFF84860064E85B /* DataStack+Observing.swift in Sources */,
|
||||||
B501FDDD1CA8D05000BE22EF /* CSSectionBy.swift in Sources */,
|
B501FDDD1CA8D05000BE22EF /* CSSectionBy.swift in Sources */,
|
||||||
|
B59FA0AE1CCBAC95007C9BCA /* ICloudStore.swift in Sources */,
|
||||||
B5E84EF81AFF846E0064E85B /* CoreStore+Transaction.swift in Sources */,
|
B5E84EF81AFF846E0064E85B /* CoreStore+Transaction.swift in Sources */,
|
||||||
B5E84F301AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
B5E84F301AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||||
B546F9691C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
B546F9691C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||||
@@ -1705,6 +1721,7 @@
|
|||||||
B5ECDC2B1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
B5ECDC2B1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
||||||
82BA18A11C4BBD1D00A0916E /* CoreStore.swift in Sources */,
|
82BA18A11C4BBD1D00A0916E /* CoreStore.swift in Sources */,
|
||||||
B546F9591C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
B546F9591C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
||||||
|
B5677D3F1CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */,
|
||||||
82BA18CF1C4BBD7100A0916E /* Functions.swift in Sources */,
|
82BA18CF1C4BBD7100A0916E /* Functions.swift in Sources */,
|
||||||
82BA18A31C4BBD2200A0916E /* DataStack.swift in Sources */,
|
82BA18A31C4BBD2200A0916E /* DataStack.swift in Sources */,
|
||||||
82BA18C81C4BBD5900A0916E /* MigrationChain.swift in Sources */,
|
82BA18C81C4BBD5900A0916E /* MigrationChain.swift in Sources */,
|
||||||
@@ -1762,6 +1779,7 @@
|
|||||||
82BA18CB1C4BBD6400A0916E /* NSManagedObject+Convenience.swift in Sources */,
|
82BA18CB1C4BBD6400A0916E /* NSManagedObject+Convenience.swift in Sources */,
|
||||||
82BA18B51C4BBD3F00A0916E /* BaseDataTransaction+Querying.swift in Sources */,
|
82BA18B51C4BBD3F00A0916E /* BaseDataTransaction+Querying.swift in Sources */,
|
||||||
B501FDDF1CA8D05000BE22EF /* CSSectionBy.swift in Sources */,
|
B501FDDF1CA8D05000BE22EF /* CSSectionBy.swift in Sources */,
|
||||||
|
B59FA0B01CCBACA7007C9BCA /* ICloudStore.swift in Sources */,
|
||||||
82BA18D31C4BBD7100A0916E /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
82BA18D31C4BBD7100A0916E /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||||
82BA18AD1C4BBD3100A0916E /* UnsafeDataTransaction.swift in Sources */,
|
82BA18AD1C4BBD3100A0916E /* UnsafeDataTransaction.swift in Sources */,
|
||||||
B546F96A1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
B546F96A1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||||
@@ -1803,6 +1821,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
B5DBE2D01C9914A900B5CEFA /* CSCoreStore.swift in Sources */,
|
B5DBE2D01C9914A900B5CEFA /* CSCoreStore.swift in Sources */,
|
||||||
|
B5677D411CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */,
|
||||||
B52DD1BE1BE1F94300949AFE /* NSProgress+Convenience.swift in Sources */,
|
B52DD1BE1BE1F94300949AFE /* NSProgress+Convenience.swift in Sources */,
|
||||||
B5ECDC151CA816E500C7F112 /* CSTweak.swift in Sources */,
|
B5ECDC151CA816E500C7F112 /* CSTweak.swift in Sources */,
|
||||||
B546F9761C9C553300D5AC55 /* SetupResult.swift in Sources */,
|
B546F9761C9C553300D5AC55 /* SetupResult.swift in Sources */,
|
||||||
@@ -1870,6 +1889,7 @@
|
|||||||
B52DD1CB1BE1F94600949AFE /* WeakObject.swift in Sources */,
|
B52DD1CB1BE1F94600949AFE /* WeakObject.swift in Sources */,
|
||||||
B52DD1C11BE1F94600949AFE /* Functions.swift in Sources */,
|
B52DD1C11BE1F94600949AFE /* Functions.swift in Sources */,
|
||||||
B53FBA0F1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
B53FBA0F1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */,
|
||||||
|
B59FA0B21CCBACA8007C9BCA /* ICloudStore.swift in Sources */,
|
||||||
B52DD19A1BE1F92800949AFE /* CoreStore+Logging.swift in Sources */,
|
B52DD19A1BE1F92800949AFE /* CoreStore+Logging.swift in Sources */,
|
||||||
B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */,
|
B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */,
|
||||||
B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||||
@@ -1944,6 +1964,7 @@
|
|||||||
B5ECDC2C1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
B5ECDC2C1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */,
|
||||||
B56321911BD65216006C9394 /* BaseDataTransaction+Importing.swift in Sources */,
|
B56321911BD65216006C9394 /* BaseDataTransaction+Importing.swift in Sources */,
|
||||||
B546F95A1C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
B546F95A1C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */,
|
||||||
|
B5677D401CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */,
|
||||||
B56321941BD65216006C9394 /* CoreStore+Querying.swift in Sources */,
|
B56321941BD65216006C9394 /* CoreStore+Querying.swift in Sources */,
|
||||||
B56321811BD65216006C9394 /* DataStack.swift in Sources */,
|
B56321811BD65216006C9394 /* DataStack.swift in Sources */,
|
||||||
B56321A81BD65219006C9394 /* NSManagedObject+Convenience.swift in Sources */,
|
B56321A81BD65219006C9394 /* NSManagedObject+Convenience.swift in Sources */,
|
||||||
@@ -2001,6 +2022,7 @@
|
|||||||
B56321851BD65216006C9394 /* CoreStore+Logging.swift in Sources */,
|
B56321851BD65216006C9394 /* CoreStore+Logging.swift in Sources */,
|
||||||
B56321921BD65216006C9394 /* BaseDataTransaction+Querying.swift in Sources */,
|
B56321921BD65216006C9394 /* BaseDataTransaction+Querying.swift in Sources */,
|
||||||
B501FDE01CA8D05000BE22EF /* CSSectionBy.swift in Sources */,
|
B501FDE01CA8D05000BE22EF /* CSSectionBy.swift in Sources */,
|
||||||
|
B59FA0B11CCBACA7007C9BCA /* ICloudStore.swift in Sources */,
|
||||||
B56321B11BD6521C006C9394 /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
B56321B11BD6521C006C9394 /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||||
B563218D1BD65216006C9394 /* CoreStore+Transaction.swift in Sources */,
|
B563218D1BD65216006C9394 /* CoreStore+Transaction.swift in Sources */,
|
||||||
B546F96B1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
B546F96B1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||||
@@ -2047,6 +2069,7 @@
|
|||||||
B5ECDC3E1CA836BE00C7F112 /* CSCoreStore+Setup.swift in Sources */,
|
B5ECDC3E1CA836BE00C7F112 /* CSCoreStore+Setup.swift in Sources */,
|
||||||
B5D9E2F31CA2C317007A9D52 /* CoreStoreError.swift in Sources */,
|
B5D9E2F31CA2C317007A9D52 /* CoreStoreError.swift in Sources */,
|
||||||
B5D9E2F41CA2C317007A9D52 /* Where.swift in Sources */,
|
B5D9E2F41CA2C317007A9D52 /* Where.swift in Sources */,
|
||||||
|
B5677D3E1CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */,
|
||||||
B5D9E2F51CA2C317007A9D52 /* FetchedResultsControllerDelegate.swift in Sources */,
|
B5D9E2F51CA2C317007A9D52 /* FetchedResultsControllerDelegate.swift in Sources */,
|
||||||
B5D9E2F61CA2C317007A9D52 /* MigrationType.swift in Sources */,
|
B5D9E2F61CA2C317007A9D52 /* MigrationType.swift in Sources */,
|
||||||
B5D9E2F71CA2C317007A9D52 /* DataStack+Querying.swift in Sources */,
|
B5D9E2F71CA2C317007A9D52 /* DataStack+Querying.swift in Sources */,
|
||||||
@@ -2132,6 +2155,7 @@
|
|||||||
B5D9E3211CA2C317007A9D52 /* CoreStore+Transaction.swift in Sources */,
|
B5D9E3211CA2C317007A9D52 /* CoreStore+Transaction.swift in Sources */,
|
||||||
B5D9E3221CA2C317007A9D52 /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
B5D9E3221CA2C317007A9D52 /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||||
B5D9E3481CA2C6C4007A9D52 /* GCDTimer.swift in Sources */,
|
B5D9E3481CA2C6C4007A9D52 /* GCDTimer.swift in Sources */,
|
||||||
|
B59FA0AF1CCBACA6007C9BCA /* ICloudStore.swift in Sources */,
|
||||||
B5D9E3231CA2C317007A9D52 /* CoreStore+Observing.swift in Sources */,
|
B5D9E3231CA2C317007A9D52 /* CoreStore+Observing.swift in Sources */,
|
||||||
B5ECDC3D1CA836BA00C7F112 /* CSError.swift in Sources */,
|
B5ECDC3D1CA836BA00C7F112 /* CSError.swift in Sources */,
|
||||||
B5D9E3241CA2C317007A9D52 /* BaseDataTransaction+Importing.swift in Sources */,
|
B5D9E3241CA2C317007A9D52 /* BaseDataTransaction+Importing.swift in Sources */,
|
||||||
|
|||||||
@@ -459,7 +459,7 @@
|
|||||||
INFOPLIST_FILE = CoreStoreDemo/Info.plist;
|
INFOPLIST_FILE = CoreStoreDemo/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.CoreStoreDemo;
|
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.corestore.demo;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@@ -471,7 +471,7 @@
|
|||||||
INFOPLIST_FILE = CoreStoreDemo/Info.plist;
|
INFOPLIST_FILE = CoreStoreDemo/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.CoreStoreDemo;
|
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.corestore.demo;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>CoreStore</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@@ -20,8 +22,6 @@
|
|||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>CoreStore</string>
|
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
|
|||||||
@@ -68,7 +68,22 @@ internal extension NSManagedObjectContext {
|
|||||||
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||||
context.undoManager = nil
|
context.undoManager = nil
|
||||||
context.setupForCoreStoreWithContextName("com.corestore.rootcontext")
|
context.setupForCoreStoreWithContextName("com.corestore.rootcontext")
|
||||||
|
context.observerForDidImportUbiquitousContentChangesNotification = NotificationObserver(
|
||||||
|
notificationName: NSPersistentStoreDidImportUbiquitousContentChangesNotification,
|
||||||
|
object: coordinator,
|
||||||
|
closure: { [weak context] (note) -> Void in
|
||||||
|
|
||||||
|
context?.performBlock { () -> Void in
|
||||||
|
|
||||||
|
let updatedObjectIDs = (note.userInfo?[NSUpdatedObjectsKey] as? Set<NSManagedObjectID>) ?? []
|
||||||
|
for objectID in updatedObjectIDs {
|
||||||
|
|
||||||
|
context?.objectWithID(objectID).willAccessValueForKey(nil)
|
||||||
|
}
|
||||||
|
context?.mergeChangesFromContextDidSaveNotification(note)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +111,6 @@ internal extension NSManagedObjectContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,6 +121,7 @@ internal extension NSManagedObjectContext {
|
|||||||
|
|
||||||
static var parentStack: Void?
|
static var parentStack: Void?
|
||||||
static var observerForDidSaveNotification: Void?
|
static var observerForDidSaveNotification: Void?
|
||||||
|
static var observerForDidImportUbiquitousContentChangesNotification: Void?
|
||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@nonobjc
|
||||||
@@ -128,4 +143,24 @@ internal extension NSManagedObjectContext {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@nonobjc
|
||||||
|
private var observerForDidImportUbiquitousContentChangesNotification: NotificationObserver? {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
|
return getAssociatedObjectForKey(
|
||||||
|
&PropertyKeys.observerForDidImportUbiquitousContentChangesNotification,
|
||||||
|
inObject: self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
newValue,
|
||||||
|
forKey: &PropertyKeys.observerForDidImportUbiquitousContentChangesNotification,
|
||||||
|
inObject: self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,6 +283,126 @@ public extension DataStack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Asynchronously adds a `CloudStorage` to the stack. Migrations are also initiated by default.
|
||||||
|
```
|
||||||
|
try dataStack.addStorage(
|
||||||
|
ICloudStore(
|
||||||
|
ubiquitousContentName: "MyAppCloudData",
|
||||||
|
ubiquitousContentTransactionLogsSubdirectory: "logs/config1",
|
||||||
|
ubiquitousContainerID: "iCloud.com.mycompany.myapp.containername",
|
||||||
|
ubiquitousPeerToken: "9614d658014f4151a95d8048fb717cf0",
|
||||||
|
configuration: "Config1",
|
||||||
|
cloudStorageOptions: .AllowSynchronousLightweightMigration
|
||||||
|
),
|
||||||
|
completion: { result in
|
||||||
|
switch result {
|
||||||
|
case .Success(let storage): // ...
|
||||||
|
case .Failure(let error): // ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- parameter storage: the cloud 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.
|
||||||
|
- throws: a `CoreStoreError` value indicating the failure
|
||||||
|
*/
|
||||||
|
public func addStorage<T: CloudStorage>(storage: T, completion: (SetupResult<T>) -> Void) throws {
|
||||||
|
|
||||||
|
let cacheFileURL = storage.cacheFileURL
|
||||||
|
try self.coordinator.performSynchronously {
|
||||||
|
|
||||||
|
if let _ = self.persistentStoreForStorage(storage) {
|
||||||
|
|
||||||
|
GCDQueue.Main.async {
|
||||||
|
|
||||||
|
completion(SetupResult(storage))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let persistentStore = self.coordinator.persistentStoreForURL(cacheFileURL) {
|
||||||
|
|
||||||
|
if let existingStorage = persistentStore.storageInterface as? T
|
||||||
|
where storage.matchesPersistentStore(persistentStore) {
|
||||||
|
|
||||||
|
GCDQueue.Main.async {
|
||||||
|
|
||||||
|
completion(SetupResult(existingStorage))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let error = CoreStoreError.DifferentStorageExistsAtURL(existingPersistentStoreURL: cacheFileURL)
|
||||||
|
CoreStore.log(
|
||||||
|
error,
|
||||||
|
"Failed to add \(typeName(storage)) at \"\(cacheFileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
|
||||||
|
)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
var cloudStorageOptions = storage.cloudStorageOptions
|
||||||
|
cloudStorageOptions.remove(.RecreateLocalStoreOnModelMismatch)
|
||||||
|
|
||||||
|
let storeOptions = storage.storeOptionsForOptions(cloudStorageOptions)
|
||||||
|
do {
|
||||||
|
|
||||||
|
try NSFileManager.defaultManager().createDirectoryAtURL(
|
||||||
|
cacheFileURL.URLByDeletingLastPathComponent!,
|
||||||
|
withIntermediateDirectories: true,
|
||||||
|
attributes: nil
|
||||||
|
)
|
||||||
|
try self.createPersistentStoreFromStorage(
|
||||||
|
storage,
|
||||||
|
finalURL: cacheFileURL,
|
||||||
|
finalStoreOptions: storeOptions
|
||||||
|
)
|
||||||
|
GCDQueue.Main.async {
|
||||||
|
|
||||||
|
completion(SetupResult(storage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch let error as NSError where storage.cloudStorageOptions.contains(.RecreateLocalStoreOnModelMismatch) && error.isCoreDataMigrationError {
|
||||||
|
|
||||||
|
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
|
||||||
|
storage.dynamicType.storeType,
|
||||||
|
URL: cacheFileURL,
|
||||||
|
options: storeOptions
|
||||||
|
)
|
||||||
|
try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait)
|
||||||
|
|
||||||
|
try self.createPersistentStoreFromStorage(
|
||||||
|
storage,
|
||||||
|
finalURL: cacheFileURL,
|
||||||
|
finalStoreOptions: storeOptions
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch let error as NSError
|
||||||
|
where error.code == NSFileReadNoSuchFileError && error.domain == NSCocoaErrorDomain {
|
||||||
|
|
||||||
|
try self.addStorageAndWait(storage)
|
||||||
|
|
||||||
|
GCDQueue.Main.async {
|
||||||
|
|
||||||
|
completion(SetupResult(storage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
let storeError = CoreStoreError(error)
|
||||||
|
CoreStore.log(
|
||||||
|
storeError,
|
||||||
|
"Failed to load \(typeName(NSPersistentStore)) metadata."
|
||||||
|
)
|
||||||
|
throw storeError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Migrates a local storage to match the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
|
Migrates a local storage to match the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
|
||||||
|
|
||||||
|
|||||||
@@ -634,19 +634,6 @@ public final class ListMonitor<T: NSManagedObject>: Hashable {
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal var willChangeListKey: Void?
|
|
||||||
internal var didChangeListKey: Void?
|
|
||||||
internal var willRefetchListKey: Void?
|
|
||||||
internal var didRefetchListKey: Void?
|
|
||||||
|
|
||||||
internal var didInsertObjectKey: Void?
|
|
||||||
internal var didDeleteObjectKey: Void?
|
|
||||||
internal var didUpdateObjectKey: Void?
|
|
||||||
internal var didMoveObjectKey: Void?
|
|
||||||
|
|
||||||
internal var didInsertSectionKey: Void?
|
|
||||||
internal var didDeleteSectionKey: Void?
|
|
||||||
|
|
||||||
internal convenience init(dataStack: DataStack, from: From<T>, sectionBy: SectionBy?, applyFetchClauses: (fetchRequest: NSFetchRequest) -> Void) {
|
internal convenience init(dataStack: DataStack, from: From<T>, sectionBy: SectionBy?, applyFetchClauses: (fetchRequest: NSFetchRequest) -> Void) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
@@ -1049,6 +1036,19 @@ public final class ListMonitor<T: NSManagedObject>: Hashable {
|
|||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
|
private var willChangeListKey: Void?
|
||||||
|
private var didChangeListKey: Void?
|
||||||
|
private var willRefetchListKey: Void?
|
||||||
|
private var didRefetchListKey: Void?
|
||||||
|
|
||||||
|
private var didInsertObjectKey: Void?
|
||||||
|
private var didDeleteObjectKey: Void?
|
||||||
|
private var didUpdateObjectKey: Void?
|
||||||
|
private var didMoveObjectKey: Void?
|
||||||
|
|
||||||
|
private var didInsertSectionKey: Void?
|
||||||
|
private var didDeleteSectionKey: Void?
|
||||||
|
|
||||||
private let fetchedResultsController: CoreStoreFetchedResultsController
|
private let fetchedResultsController: CoreStoreFetchedResultsController
|
||||||
private let fetchedResultsControllerDelegate: FetchedResultsControllerDelegate
|
private let fetchedResultsControllerDelegate: FetchedResultsControllerDelegate
|
||||||
private let sectionIndexTransformer: (sectionName: KeyPath?) -> String?
|
private let sectionIndexTransformer: (sectionName: KeyPath?) -> String?
|
||||||
@@ -1320,18 +1320,18 @@ public func == <T: NSManagedObject, U: NSManagedObject>(lhs: ListMonitor<T>, rhs
|
|||||||
|
|
||||||
// MARK: - Notification Keys
|
// MARK: - Notification Keys
|
||||||
|
|
||||||
internal let ListMonitorWillChangeListNotification = "ListMonitorWillChangeListNotification"
|
private let ListMonitorWillChangeListNotification = "ListMonitorWillChangeListNotification"
|
||||||
internal let ListMonitorDidChangeListNotification = "ListMonitorDidChangeListNotification"
|
private let ListMonitorDidChangeListNotification = "ListMonitorDidChangeListNotification"
|
||||||
internal let ListMonitorWillRefetchListNotification = "ListMonitorWillRefetchListNotification"
|
private let ListMonitorWillRefetchListNotification = "ListMonitorWillRefetchListNotification"
|
||||||
internal let ListMonitorDidRefetchListNotification = "ListMonitorDidRefetchListNotification"
|
private let ListMonitorDidRefetchListNotification = "ListMonitorDidRefetchListNotification"
|
||||||
|
|
||||||
internal let ListMonitorDidInsertObjectNotification = "ListMonitorDidInsertObjectNotification"
|
private let ListMonitorDidInsertObjectNotification = "ListMonitorDidInsertObjectNotification"
|
||||||
internal let ListMonitorDidDeleteObjectNotification = "ListMonitorDidDeleteObjectNotification"
|
private let ListMonitorDidDeleteObjectNotification = "ListMonitorDidDeleteObjectNotification"
|
||||||
internal let ListMonitorDidUpdateObjectNotification = "ListMonitorDidUpdateObjectNotification"
|
private let ListMonitorDidUpdateObjectNotification = "ListMonitorDidUpdateObjectNotification"
|
||||||
internal let ListMonitorDidMoveObjectNotification = "ListMonitorDidMoveObjectNotification"
|
private let ListMonitorDidMoveObjectNotification = "ListMonitorDidMoveObjectNotification"
|
||||||
|
|
||||||
internal let ListMonitorDidInsertSectionNotification = "ListMonitorDidInsertSectionNotification"
|
private let ListMonitorDidInsertSectionNotification = "ListMonitorDidInsertSectionNotification"
|
||||||
internal let ListMonitorDidDeleteSectionNotification = "ListMonitorDidDeleteSectionNotification"
|
private let ListMonitorDidDeleteSectionNotification = "ListMonitorDidDeleteSectionNotification"
|
||||||
|
|
||||||
private let UserInfoKeyObject = "UserInfoKeyObject"
|
private let UserInfoKeyObject = "UserInfoKeyObject"
|
||||||
private let UserInfoKeyIndexPath = "UserInfoKeyIndexPath"
|
private let UserInfoKeyIndexPath = "UserInfoKeyIndexPath"
|
||||||
|
|||||||
@@ -131,6 +131,30 @@ public extension CoreStore {
|
|||||||
return try self.defaultStack.addStorageAndWait(storage)
|
return try self.defaultStack.addStorageAndWait(storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds a `CloudStorage` to the `defaultStack` and blocks until completion.
|
||||||
|
```
|
||||||
|
try CoreStore.addStorageAndWait(
|
||||||
|
ICloudStore(
|
||||||
|
ubiquitousContentName: "MyAppCloudData",
|
||||||
|
ubiquitousContentTransactionLogsSubdirectory: "logs/config1",
|
||||||
|
ubiquitousContainerID: "iCloud.com.mycompany.myapp.containername",
|
||||||
|
ubiquitousPeerToken: "9614d658014f4151a95d8048fb717cf0",
|
||||||
|
configuration: "Config1",
|
||||||
|
cloudStorageOptions: .AllowSynchronousLightweightMigration
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- parameter storage: the local storage
|
||||||
|
- throws: a `CoreStoreError` value indicating the failure
|
||||||
|
- returns: the cloud storage added to the stack. Note that this may not always be the same instance as the parameter argument if a previous `CloudStorage` was already added at the same URL and with the same configuration.
|
||||||
|
*/
|
||||||
|
public static func addStorageAndWait<T: CloudStorage>(storage: T) throws -> T {
|
||||||
|
|
||||||
|
return try self.defaultStack.addStorageAndWait(storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Deprecated
|
// MARK: Deprecated
|
||||||
|
|
||||||
|
|||||||
@@ -238,12 +238,10 @@ public final class DataStack {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
var storeOptions = storage.storeOptions ?? [:]
|
var localStorageOptions = storage.localStorageOptions
|
||||||
if storage.localStorageOptions.contains(.AllowSynchronousLightweightMigration) {
|
localStorageOptions.remove(.RecreateStoreOnModelMismatch)
|
||||||
|
|
||||||
storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true
|
let storeOptions = storage.storeOptionsForOptions(localStorageOptions)
|
||||||
storeOptions[NSInferMappingModelAutomaticallyOption] = true
|
|
||||||
}
|
|
||||||
do {
|
do {
|
||||||
|
|
||||||
try NSFileManager.defaultManager().createDirectoryAtURL(
|
try NSFileManager.defaultManager().createDirectoryAtURL(
|
||||||
@@ -287,6 +285,100 @@ public final class DataStack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds a `CloudStorage` to the stack and blocks until completion.
|
||||||
|
```
|
||||||
|
try dataStack.addStorageAndWait(
|
||||||
|
ICloudStore(
|
||||||
|
ubiquitousContentName: "MyAppCloudData",
|
||||||
|
ubiquitousContentTransactionLogsSubdirectory: "logs/config1",
|
||||||
|
ubiquitousContainerID: "iCloud.com.mycompany.myapp.containername",
|
||||||
|
ubiquitousPeerToken: "9614d658014f4151a95d8048fb717cf0",
|
||||||
|
configuration: "Config1",
|
||||||
|
cloudStorageOptions: .AllowSynchronousLightweightMigration
|
||||||
|
)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- parameter storage: the local storage
|
||||||
|
- throws: a `CoreStoreError` value indicating the failure
|
||||||
|
- returns: the cloud storage added to the stack. Note that this may not always be the same instance as the parameter argument if a previous `CloudStorage` was already added at the same URL and with the same configuration.
|
||||||
|
*/
|
||||||
|
public func addStorageAndWait<T: CloudStorage>(storage: T) throws -> T {
|
||||||
|
|
||||||
|
return try self.coordinator.performSynchronously {
|
||||||
|
|
||||||
|
if let _ = self.persistentStoreForStorage(storage) {
|
||||||
|
|
||||||
|
return storage
|
||||||
|
}
|
||||||
|
|
||||||
|
let cacheFileURL = storage.cacheFileURL
|
||||||
|
if let persistentStore = self.coordinator.persistentStoreForURL(cacheFileURL) {
|
||||||
|
|
||||||
|
if let existingStorage = persistentStore.storageInterface as? T
|
||||||
|
where storage.matchesPersistentStore(persistentStore) {
|
||||||
|
|
||||||
|
return existingStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
let error = CoreStoreError.DifferentStorageExistsAtURL(existingPersistentStoreURL: cacheFileURL)
|
||||||
|
CoreStore.log(
|
||||||
|
error,
|
||||||
|
"Failed to add \(typeName(storage)) at \"\(cacheFileURL)\" because a different \(typeName(NSPersistentStore)) at that URL already exists."
|
||||||
|
)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
var cloudStorageOptions = storage.cloudStorageOptions
|
||||||
|
cloudStorageOptions.remove(.RecreateLocalStoreOnModelMismatch)
|
||||||
|
|
||||||
|
let storeOptions = storage.storeOptionsForOptions(cloudStorageOptions)
|
||||||
|
do {
|
||||||
|
|
||||||
|
try NSFileManager.defaultManager().createDirectoryAtURL(
|
||||||
|
cacheFileURL.URLByDeletingLastPathComponent!,
|
||||||
|
withIntermediateDirectories: true,
|
||||||
|
attributes: nil
|
||||||
|
)
|
||||||
|
try self.createPersistentStoreFromStorage(
|
||||||
|
storage,
|
||||||
|
finalURL: cacheFileURL,
|
||||||
|
finalStoreOptions: storeOptions
|
||||||
|
)
|
||||||
|
return storage
|
||||||
|
}
|
||||||
|
catch let error as NSError where storage.cloudStorageOptions.contains(.RecreateLocalStoreOnModelMismatch) && error.isCoreDataMigrationError {
|
||||||
|
|
||||||
|
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
|
||||||
|
storage.dynamicType.storeType,
|
||||||
|
URL: cacheFileURL,
|
||||||
|
options: storeOptions
|
||||||
|
)
|
||||||
|
try _ = self.model[metadata].flatMap(storage.eraseStorageAndWait)
|
||||||
|
|
||||||
|
try self.createPersistentStoreFromStorage(
|
||||||
|
storage,
|
||||||
|
finalURL: cacheFileURL,
|
||||||
|
finalStoreOptions: storeOptions
|
||||||
|
)
|
||||||
|
return storage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
|
||||||
|
let storeError = CoreStoreError(error)
|
||||||
|
CoreStore.log(
|
||||||
|
storeError,
|
||||||
|
"Failed to add \(typeName(storage)) to the stack."
|
||||||
|
)
|
||||||
|
throw storeError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
@@ -407,6 +499,7 @@ public final class DataStack {
|
|||||||
self.entityConfigurationsMapping[managedObjectClassName]?.insert(configurationName)
|
self.entityConfigurationsMapping[managedObjectClassName]?.insert(configurationName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
storage.didAddToDataStack(self)
|
||||||
return persistentStore
|
return persistentStore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
494
Sources/Setup/StorageInterfaces/ICloudStore.swift
Normal file
494
Sources/Setup/StorageInterfaces/ICloudStore.swift
Normal file
@@ -0,0 +1,494 @@
|
|||||||
|
//
|
||||||
|
// ICloudStore.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: - ICloudStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
A storage interface backed by an SQLite database managed by iCloud.
|
||||||
|
*/
|
||||||
|
public class ICloudStore: CloudStorage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initializes an iCloud store interface from the given ubiquitous store information. Returns `nil` if the container could not be located or if iCloud storage is unavailable for the current user or device
|
||||||
|
```
|
||||||
|
try CoreStore.addStorage(
|
||||||
|
ICloudStore(
|
||||||
|
ubiquitousContentName: "MyAppCloudData",
|
||||||
|
ubiquitousContentTransactionLogsSubdirectory: "logs/config1",
|
||||||
|
ubiquitousContainerID: "iCloud.com.mycompany.myapp.containername",
|
||||||
|
ubiquitousPeerToken: "9614d658014f4151a95d8048fb717cf0",
|
||||||
|
configuration: "Config1",
|
||||||
|
cloudStorageOptions: .AllowSynchronousLightweightMigration
|
||||||
|
)
|
||||||
|
completion: { result in
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- parameter ubiquitousContentName: the name of the store in iCloud. This is required and should not be empty, and should not contain periods (`.`).
|
||||||
|
- parameter ubiquitousContentTransactionLogsSubdirectory: an optional subdirectory path for the transaction logs
|
||||||
|
- parameter ubiquitousContainerID: a container if your app has multiple ubiquity container identifiers in its entitlements
|
||||||
|
- parameter ubiquitousPeerToken: a per-application salt to allow multiple apps on the same device to share a Core Data store integrated with iCloud
|
||||||
|
- 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 `ubiquitousContentName` explicitly for each of them.
|
||||||
|
- parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration.
|
||||||
|
- parameter cloudStorageOptions: When the `ICloudStore` is passed to the `DataStack`'s `addStorage()` methods, tells the `DataStack` how to setup the persistent store. Defaults to `.None`.
|
||||||
|
*/
|
||||||
|
public required init?(ubiquitousContentName: String, ubiquitousContentTransactionLogsSubdirectory: String? = nil, ubiquitousContainerID: String? = nil, ubiquitousPeerToken: String? = nil, configuration: String? = nil, cloudStorageOptions: CloudStorageOptions = nil) {
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
!ubiquitousContentName.isEmpty,
|
||||||
|
"The ubiquitousContentName cannot be empty."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
!ubiquitousContentName.containsString("."),
|
||||||
|
"The ubiquitousContentName cannot contain periods."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
ubiquitousContentTransactionLogsSubdirectory?.isEmpty != true,
|
||||||
|
"The ubiquitousContentURLRelativePath should not be empty if provided."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
ubiquitousPeerToken?.isEmpty != true,
|
||||||
|
"The ubiquitousPeerToken should not be empty if provided."
|
||||||
|
)
|
||||||
|
|
||||||
|
let fileManager = NSFileManager.defaultManager()
|
||||||
|
guard let cacheFileURL = fileManager.URLForUbiquityContainerIdentifier(ubiquitousContainerID) else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var storeOptions: [String: AnyObject] = [
|
||||||
|
NSSQLitePragmasOption: ["journal_mode": "WAL"],
|
||||||
|
NSPersistentStoreUbiquitousContentNameKey: ubiquitousContentName
|
||||||
|
]
|
||||||
|
storeOptions[NSPersistentStoreUbiquitousContentURLKey] = ubiquitousContentTransactionLogsSubdirectory
|
||||||
|
storeOptions[NSPersistentStoreUbiquitousContainerIdentifierKey] = ubiquitousContainerID
|
||||||
|
storeOptions[NSPersistentStoreUbiquitousPeerTokenOption] = ubiquitousPeerToken
|
||||||
|
|
||||||
|
self.cacheFileURL = cacheFileURL
|
||||||
|
self.configuration = configuration
|
||||||
|
self.cloudStorageOptions = cloudStorageOptions
|
||||||
|
self.storeOptions = storeOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
public func addUbiquitousStoreObserver<T: ICloudStoreObserver>(observer: T) {
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
NSThread.isMainThread(),
|
||||||
|
"Attempted to add an observer of type \(typeName(observer)) outside the main thread."
|
||||||
|
)
|
||||||
|
|
||||||
|
self.removeUbiquitousStoreObserver(observer)
|
||||||
|
|
||||||
|
self.registerNotification(
|
||||||
|
&self.willFinishInitialImportKey,
|
||||||
|
name: ICloudUbiquitousStoreWillFinishInitialImportNotification,
|
||||||
|
toObserver: observer,
|
||||||
|
callback: { (observer, storage, dataStack) in
|
||||||
|
|
||||||
|
observer.iCloudStoreWillFinishUbiquitousStoreInitialImport(storage: storage, dataStack: dataStack)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.registerNotification(
|
||||||
|
&self.didFinishInitialImportKey,
|
||||||
|
name: ICloudUbiquitousStoreDidFinishInitialImportNotification,
|
||||||
|
toObserver: observer,
|
||||||
|
callback: { (observer, storage, dataStack) in
|
||||||
|
|
||||||
|
observer.iCloudStoreDidFinishUbiquitousStoreInitialImport(storage: storage, dataStack: dataStack)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.registerNotification(
|
||||||
|
&self.willAddAccountKey,
|
||||||
|
name: ICloudUbiquitousStoreWillAddAccountNotification,
|
||||||
|
toObserver: observer,
|
||||||
|
callback: { (observer, storage, dataStack) in
|
||||||
|
|
||||||
|
observer.iCloudStoreWillAddAccount(storage: storage, dataStack: dataStack)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.registerNotification(
|
||||||
|
&self.didAddAccountKey,
|
||||||
|
name: ICloudUbiquitousStoreDidAddAccountNotification,
|
||||||
|
toObserver: observer,
|
||||||
|
callback: { (observer, storage, dataStack) in
|
||||||
|
|
||||||
|
observer.iCloudStoreDidAddAccount(storage: storage, dataStack: dataStack)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.registerNotification(
|
||||||
|
&self.willRemoveAccountKey,
|
||||||
|
name: ICloudUbiquitousStoreWillRemoveAccountNotification,
|
||||||
|
toObserver: observer,
|
||||||
|
callback: { (observer, storage, dataStack) in
|
||||||
|
|
||||||
|
observer.iCloudStoreWillRemoveAccount(storage: storage, dataStack: dataStack)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.registerNotification(
|
||||||
|
&self.didRemoveAccountKey,
|
||||||
|
name: ICloudUbiquitousStoreDidRemoveAccountNotification,
|
||||||
|
toObserver: observer,
|
||||||
|
callback: { (observer, storage, dataStack) in
|
||||||
|
|
||||||
|
observer.iCloudStoreDidRemoveAccount(storage: storage, dataStack: dataStack)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.registerNotification(
|
||||||
|
&self.willRemoveContentKey,
|
||||||
|
name: ICloudUbiquitousStoreWillRemoveContentNotification,
|
||||||
|
toObserver: observer,
|
||||||
|
callback: { (observer, storage, dataStack) in
|
||||||
|
|
||||||
|
observer.iCloudStoreWillRemoveContent(storage: storage, dataStack: dataStack)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.registerNotification(
|
||||||
|
&self.didRemoveContentKey,
|
||||||
|
name: ICloudUbiquitousStoreDidRemoveContentNotification,
|
||||||
|
toObserver: observer,
|
||||||
|
callback: { (observer, storage, dataStack) in
|
||||||
|
|
||||||
|
observer.iCloudStoreDidRemoveContent(storage: storage, dataStack: dataStack)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func removeUbiquitousStoreObserver(observer: ICloudStoreObserver) {
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
NSThread.isMainThread(),
|
||||||
|
"Attempted to remove an observer of type \(typeName(observer)) outside the main thread."
|
||||||
|
)
|
||||||
|
let nilValue: AnyObject? = nil
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &self.willFinishInitialImportKey,
|
||||||
|
inObject: observer
|
||||||
|
)
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &self.didFinishInitialImportKey,
|
||||||
|
inObject: observer
|
||||||
|
)
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &self.willAddAccountKey,
|
||||||
|
inObject: observer
|
||||||
|
)
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &self.didAddAccountKey,
|
||||||
|
inObject: observer
|
||||||
|
)
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &self.willRemoveAccountKey,
|
||||||
|
inObject: observer
|
||||||
|
)
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &self.didRemoveAccountKey,
|
||||||
|
inObject: observer
|
||||||
|
)
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &self.willRemoveContentKey,
|
||||||
|
inObject: observer
|
||||||
|
)
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &self.didRemoveContentKey,
|
||||||
|
inObject: observer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: StorageInterface
|
||||||
|
|
||||||
|
/**
|
||||||
|
The string identifier for the `NSPersistentStore`'s `type` property. For `SQLiteStore`s, this is always set to `NSSQLiteStoreType`.
|
||||||
|
*/
|
||||||
|
public static let storeType = NSSQLiteStoreType
|
||||||
|
|
||||||
|
/**
|
||||||
|
The configuration name in the model file
|
||||||
|
*/
|
||||||
|
public let configuration: String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
The options dictionary for the `NSPersistentStore`. For `SQLiteStore`s, this is always set to
|
||||||
|
```
|
||||||
|
[NSSQLitePragmasOption: ["journal_mode": "WAL"]]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
public let storeOptions: [String: AnyObject]?
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
public func didAddToDataStack(dataStack: DataStack) {
|
||||||
|
|
||||||
|
self.didRemoveFromDataStack(dataStack)
|
||||||
|
|
||||||
|
self.dataStack = dataStack
|
||||||
|
let coordinator = dataStack.coordinator
|
||||||
|
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
NotificationObserver(
|
||||||
|
notificationName: NSPersistentStoreCoordinatorStoresWillChangeNotification,
|
||||||
|
object: coordinator,
|
||||||
|
closure: { [weak self, weak dataStack] (note) -> Void in
|
||||||
|
|
||||||
|
guard let `self` = self,
|
||||||
|
let dataStack = dataStack,
|
||||||
|
let userInfo = note.userInfo,
|
||||||
|
let transitionType = userInfo[NSPersistentStoreUbiquitousTransitionTypeKey] as? NSNumber else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let notification: String
|
||||||
|
switch NSPersistentStoreUbiquitousTransitionType(rawValue: transitionType.unsignedIntegerValue) {
|
||||||
|
|
||||||
|
case .InitialImportCompleted?:
|
||||||
|
notification = ICloudUbiquitousStoreWillFinishInitialImportNotification
|
||||||
|
|
||||||
|
case .AccountAdded?:
|
||||||
|
notification = ICloudUbiquitousStoreWillAddAccountNotification
|
||||||
|
|
||||||
|
case .AccountRemoved?:
|
||||||
|
notification = ICloudUbiquitousStoreWillRemoveAccountNotification
|
||||||
|
|
||||||
|
case .ContentRemoved?:
|
||||||
|
notification = ICloudUbiquitousStoreWillRemoveContentNotification
|
||||||
|
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
NSNotificationCenter.defaultCenter().postNotificationName(
|
||||||
|
notification,
|
||||||
|
object: self,
|
||||||
|
userInfo: [UserInfoKeyDataStack: dataStack]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
forKey: &Static.persistentStoreCoordinatorWillChangeStores,
|
||||||
|
inObject: self
|
||||||
|
)
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
NotificationObserver(
|
||||||
|
notificationName: NSPersistentStoreCoordinatorStoresDidChangeNotification,
|
||||||
|
object: coordinator,
|
||||||
|
closure: { [weak self, weak dataStack] (note) -> Void in
|
||||||
|
|
||||||
|
guard let `self` = self,
|
||||||
|
let dataStack = dataStack,
|
||||||
|
let userInfo = note.userInfo,
|
||||||
|
let transitionType = userInfo[NSPersistentStoreUbiquitousTransitionTypeKey] as? NSNumber else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let notification: String
|
||||||
|
switch NSPersistentStoreUbiquitousTransitionType(rawValue: transitionType.unsignedIntegerValue) {
|
||||||
|
|
||||||
|
case .InitialImportCompleted?:
|
||||||
|
notification = ICloudUbiquitousStoreDidFinishInitialImportNotification
|
||||||
|
|
||||||
|
case .AccountAdded?:
|
||||||
|
notification = ICloudUbiquitousStoreDidAddAccountNotification
|
||||||
|
|
||||||
|
case .AccountRemoved?:
|
||||||
|
notification = ICloudUbiquitousStoreDidRemoveAccountNotification
|
||||||
|
|
||||||
|
case .ContentRemoved?:
|
||||||
|
notification = ICloudUbiquitousStoreDidRemoveContentNotification
|
||||||
|
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
NSNotificationCenter.defaultCenter().postNotificationName(
|
||||||
|
notification,
|
||||||
|
object: self,
|
||||||
|
userInfo: [UserInfoKeyDataStack: dataStack]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
forKey: &Static.persistentStoreCoordinatorDidChangeStores,
|
||||||
|
inObject: self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
public func didRemoveFromDataStack(dataStack: DataStack) {
|
||||||
|
|
||||||
|
let coordinator = dataStack.coordinator
|
||||||
|
let nilValue: AnyObject? = nil
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &Static.persistentStoreCoordinatorWillChangeStores,
|
||||||
|
inObject: coordinator
|
||||||
|
)
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
nilValue,
|
||||||
|
forKey: &Static.persistentStoreCoordinatorDidChangeStores,
|
||||||
|
inObject: coordinator
|
||||||
|
)
|
||||||
|
|
||||||
|
self.dataStack = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: CloudStorage
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `NSURL` that points to the ubiquity container file
|
||||||
|
*/
|
||||||
|
public let cacheFileURL: NSURL
|
||||||
|
|
||||||
|
/**
|
||||||
|
Options that tell the `DataStack` how to setup the persistent store
|
||||||
|
*/
|
||||||
|
public var cloudStorageOptions: CloudStorageOptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
The options dictionary for the specified `CloudStorageOptions`
|
||||||
|
*/
|
||||||
|
public func storeOptionsForOptions(options: CloudStorageOptions) -> [String: AnyObject]? {
|
||||||
|
|
||||||
|
if options == .None {
|
||||||
|
|
||||||
|
return self.storeOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
var storeOptions = self.storeOptions ?? [:]
|
||||||
|
if options.contains(.AllowSynchronousLightweightMigration) {
|
||||||
|
|
||||||
|
storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true
|
||||||
|
storeOptions[NSInferMappingModelAutomaticallyOption] = true
|
||||||
|
}
|
||||||
|
if options.contains(.RecreateLocalStoreOnModelMismatch) {
|
||||||
|
|
||||||
|
storeOptions[NSPersistentStoreRebuildFromUbiquitousContentOption] = true
|
||||||
|
}
|
||||||
|
return storeOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
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. For `SQLiteStore`, this converts the database's WAL journaling mode to DELETE before deleting the file.
|
||||||
|
*/
|
||||||
|
public func eraseStorageAndWait(soureModel soureModel: NSManagedObjectModel) throws {
|
||||||
|
|
||||||
|
// TODO: check if attached to persistent store
|
||||||
|
|
||||||
|
let cacheFileURL = self.cacheFileURL
|
||||||
|
try autoreleasepool {
|
||||||
|
|
||||||
|
let journalUpdatingCoordinator = NSPersistentStoreCoordinator(managedObjectModel: soureModel)
|
||||||
|
let options = [
|
||||||
|
NSSQLitePragmasOption: ["journal_mode": "DELETE"],
|
||||||
|
NSPersistentStoreRemoveUbiquitousMetadataOption: true
|
||||||
|
]
|
||||||
|
let store = try journalUpdatingCoordinator.addPersistentStoreWithType(
|
||||||
|
self.dynamicType.storeType,
|
||||||
|
configuration: self.configuration,
|
||||||
|
URL: cacheFileURL,
|
||||||
|
options: options
|
||||||
|
)
|
||||||
|
try journalUpdatingCoordinator.removePersistentStore(store)
|
||||||
|
try NSPersistentStoreCoordinator.removeUbiquitousContentAndPersistentStoreAtURL(
|
||||||
|
cacheFileURL,
|
||||||
|
options: options
|
||||||
|
)
|
||||||
|
try NSFileManager.defaultManager().removeItemAtURL(cacheFileURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private struct Static {
|
||||||
|
|
||||||
|
private static var persistentStoreCoordinatorWillChangeStores: Void?
|
||||||
|
private static var persistentStoreCoordinatorDidChangeStores: Void?
|
||||||
|
}
|
||||||
|
|
||||||
|
private var willFinishInitialImportKey: Void?
|
||||||
|
private var didFinishInitialImportKey: Void?
|
||||||
|
private var willAddAccountKey: Void?
|
||||||
|
private var didAddAccountKey: Void?
|
||||||
|
private var willRemoveAccountKey: Void?
|
||||||
|
private var didRemoveAccountKey: Void?
|
||||||
|
private var willRemoveContentKey: Void?
|
||||||
|
private var didRemoveContentKey: Void?
|
||||||
|
|
||||||
|
private weak var dataStack: DataStack?
|
||||||
|
|
||||||
|
private func registerNotification<T: ICloudStoreObserver>(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: T, callback: (observer: T, storage: ICloudStore, dataStack: DataStack) -> Void) {
|
||||||
|
|
||||||
|
setAssociatedRetainedObject(
|
||||||
|
NotificationObserver(
|
||||||
|
notificationName: name,
|
||||||
|
object: self,
|
||||||
|
closure: { [weak self, weak observer] (note) -> Void in
|
||||||
|
|
||||||
|
guard let `self` = self,
|
||||||
|
let observer = observer,
|
||||||
|
let dataStack = note.userInfo?[UserInfoKeyDataStack] as? DataStack
|
||||||
|
where self.dataStack === dataStack else {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback(observer: observer, storage: self, dataStack: dataStack)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
forKey: notificationKey,
|
||||||
|
inObject: observer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Notification Keys
|
||||||
|
|
||||||
|
private let ICloudUbiquitousStoreWillFinishInitialImportNotification = "ICloudUbiquitousStoreWillFinishInitialImportNotification"
|
||||||
|
private let ICloudUbiquitousStoreDidFinishInitialImportNotification = "ICloudUbiquitousStoreDidFinishInitialImportNotification"
|
||||||
|
private let ICloudUbiquitousStoreWillAddAccountNotification = "ICloudUbiquitousStoreWillAddAccountNotification"
|
||||||
|
private let ICloudUbiquitousStoreDidAddAccountNotification = "ICloudUbiquitousStoreDidAddAccountNotification"
|
||||||
|
private let ICloudUbiquitousStoreWillRemoveAccountNotification = "ICloudUbiquitousStoreWillRemoveAccountNotification"
|
||||||
|
private let ICloudUbiquitousStoreDidRemoveAccountNotification = "ICloudUbiquitousStoreDidRemoveAccountNotification"
|
||||||
|
private let ICloudUbiquitousStoreWillRemoveContentNotification = "ICloudUbiquitousStoreWillRemoveContentNotification"
|
||||||
|
private let ICloudUbiquitousStoreDidRemoveContentNotification = "ICloudUbiquitousStoreDidRemoveContentNotification"
|
||||||
|
|
||||||
|
private let UserInfoKeyDataStack = "UserInfoKeyDataStack"
|
||||||
59
Sources/Setup/StorageInterfaces/ICloudStoreObserver.swift
Normal file
59
Sources/Setup/StorageInterfaces/ICloudStoreObserver.swift
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//
|
||||||
|
// ICloudStoreObserver.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
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - ICloudStoreObserver
|
||||||
|
|
||||||
|
public protocol ICloudStoreObserver: class {
|
||||||
|
|
||||||
|
func iCloudStoreWillFinishUbiquitousStoreInitialImport(storage storage: ICloudStore, dataStack: DataStack)
|
||||||
|
func iCloudStoreDidFinishUbiquitousStoreInitialImport(storage storage: ICloudStore, dataStack: DataStack)
|
||||||
|
|
||||||
|
func iCloudStoreWillAddAccount(storage storage: ICloudStore, dataStack: DataStack)
|
||||||
|
func iCloudStoreDidAddAccount(storage storage: ICloudStore, dataStack: DataStack)
|
||||||
|
|
||||||
|
func iCloudStoreWillRemoveAccount(storage storage: ICloudStore, dataStack: DataStack)
|
||||||
|
func iCloudStoreDidRemoveAccount(storage storage: ICloudStore, dataStack: DataStack)
|
||||||
|
|
||||||
|
func iCloudStoreWillRemoveContent(storage storage: ICloudStore, dataStack: DataStack)
|
||||||
|
func iCloudStoreDidRemoveContent(storage storage: ICloudStore, dataStack: DataStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension ICloudStoreObserver {
|
||||||
|
|
||||||
|
public func iCloudStoreWillFinishUbiquitousStoreInitialImport(storage storage: ICloudStore, dataStack: DataStack) {}
|
||||||
|
public func iCloudStoreDidFinishUbiquitousStoreInitialImport(storage storage: ICloudStore, dataStack: DataStack) {}
|
||||||
|
|
||||||
|
public func iCloudStoreWillAddAccount(storage storage: ICloudStore, dataStack: DataStack) {}
|
||||||
|
public func iCloudStoreDidAddAccount(storage storage: ICloudStore, dataStack: DataStack) {}
|
||||||
|
|
||||||
|
public func iCloudStoreWillRemoveAccount(storage storage: ICloudStore, dataStack: DataStack) {}
|
||||||
|
public func iCloudStoreDidRemoveAccount(storage storage: ICloudStore, dataStack: DataStack) {}
|
||||||
|
|
||||||
|
public func iCloudStoreWillRemoveContent(storage storage: ICloudStore, dataStack: DataStack) {}
|
||||||
|
public func iCloudStoreDidRemoveContent(storage storage: ICloudStore, dataStack: DataStack) {}
|
||||||
|
}
|
||||||
@@ -70,4 +70,25 @@ public final class InMemoryStore: StorageInterface, DefaultInitializableStore {
|
|||||||
The options dictionary for the `NSPersistentStore`. For `InMemoryStore`s, this is always set to `nil`.
|
The options dictionary for the `NSPersistentStore`. For `InMemoryStore`s, this is always set to `nil`.
|
||||||
*/
|
*/
|
||||||
public let storeOptions: [String: AnyObject]? = nil
|
public let storeOptions: [String: AnyObject]? = nil
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
public func didAddToDataStack(dataStack: DataStack) {
|
||||||
|
|
||||||
|
self.dataStack = dataStack
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
public func didRemoveFromDataStack(dataStack: DataStack) {
|
||||||
|
|
||||||
|
self.dataStack = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private weak var dataStack: DataStack?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,62 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: StorageInterface
|
||||||
|
|
||||||
|
/**
|
||||||
|
The string identifier for the `NSPersistentStore`'s `type` property. For `SQLiteStore`s, this is always set to `NSSQLiteStoreType`.
|
||||||
|
*/
|
||||||
|
public static let storeType = NSSQLiteStoreType
|
||||||
|
|
||||||
|
/**
|
||||||
|
The options dictionary for the specified `LocalStorageOptions`
|
||||||
|
*/
|
||||||
|
public func storeOptionsForOptions(options: LocalStorageOptions) -> [String: AnyObject]? {
|
||||||
|
|
||||||
|
if options == .None {
|
||||||
|
|
||||||
|
return self.storeOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
var storeOptions = self.storeOptions ?? [:]
|
||||||
|
if options.contains(.AllowSynchronousLightweightMigration) {
|
||||||
|
|
||||||
|
storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true
|
||||||
|
storeOptions[NSInferMappingModelAutomaticallyOption] = true
|
||||||
|
}
|
||||||
|
return storeOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The configuration name in the model file
|
||||||
|
*/
|
||||||
|
public let configuration: String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
The options dictionary for the `NSPersistentStore`. For `SQLiteStore`s, this is always set to
|
||||||
|
```
|
||||||
|
[NSSQLitePragmasOption: ["journal_mode": "WAL"]]
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
public let storeOptions: [String: AnyObject]? = [NSSQLitePragmasOption: ["journal_mode": "WAL"]]
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
public func didAddToDataStack(dataStack: DataStack) {
|
||||||
|
|
||||||
|
self.dataStack = dataStack
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
public func didRemoveFromDataStack(dataStack: DataStack) {
|
||||||
|
|
||||||
|
self.dataStack = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// MAKR: LocalStorage
|
// MAKR: LocalStorage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,27 +161,6 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore {
|
|||||||
*/
|
*/
|
||||||
public var localStorageOptions: LocalStorageOptions
|
public var localStorageOptions: LocalStorageOptions
|
||||||
|
|
||||||
|
|
||||||
// MARK: StorageInterface
|
|
||||||
|
|
||||||
/**
|
|
||||||
The string identifier for the `NSPersistentStore`'s `type` property. For `SQLiteStore`s, this is always set to `NSSQLiteStoreType`.
|
|
||||||
*/
|
|
||||||
public static let storeType = NSSQLiteStoreType
|
|
||||||
|
|
||||||
/**
|
|
||||||
The configuration name in the model file
|
|
||||||
*/
|
|
||||||
public let configuration: String?
|
|
||||||
|
|
||||||
/**
|
|
||||||
The options dictionary for the `NSPersistentStore`. For `SQLiteStore`s, this is always set to
|
|
||||||
```
|
|
||||||
[NSSQLitePragmasOption: ["journal_mode": "WAL"]]
|
|
||||||
```
|
|
||||||
*/
|
|
||||||
public let storeOptions: [String: AnyObject]? = [NSSQLitePragmasOption: ["journal_mode": "WAL"]]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
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. For `SQLiteStore`, this converts the database's WAL journaling mode to DELETE before deleting the file.
|
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. For `SQLiteStore`, this converts the database's WAL journaling mode to DELETE before deleting the file.
|
||||||
*/
|
*/
|
||||||
@@ -168,4 +203,9 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore {
|
|||||||
internal static let defaultFileURL = LegacySQLiteStore.defaultRootDirectory
|
internal static let defaultFileURL = LegacySQLiteStore.defaultRootDirectory
|
||||||
.URLByAppendingPathComponent(DataStack.applicationName, isDirectory: false)
|
.URLByAppendingPathComponent(DataStack.applicationName, isDirectory: false)
|
||||||
.URLByAppendingPathExtension("sqlite")
|
.URLByAppendingPathExtension("sqlite")
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private weak var dataStack: DataStack?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,24 +86,6 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MAKR: LocalStorage
|
|
||||||
|
|
||||||
/**
|
|
||||||
The `NSURL` that points to the SQLite file
|
|
||||||
*/
|
|
||||||
public let fileURL: NSURL
|
|
||||||
|
|
||||||
/**
|
|
||||||
The `NSBundle`s from which to search mapping models for migrations
|
|
||||||
*/
|
|
||||||
public let mappingModelBundles: [NSBundle]
|
|
||||||
|
|
||||||
/**
|
|
||||||
Options that tell the `DataStack` how to setup the persistent store
|
|
||||||
*/
|
|
||||||
public var localStorageOptions: LocalStorageOptions
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: StorageInterface
|
// MARK: StorageInterface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,6 +106,59 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore {
|
|||||||
*/
|
*/
|
||||||
public let storeOptions: [String: AnyObject]? = [NSSQLitePragmasOption: ["journal_mode": "WAL"]]
|
public let storeOptions: [String: AnyObject]? = [NSSQLitePragmasOption: ["journal_mode": "WAL"]]
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
public func didAddToDataStack(dataStack: DataStack) {
|
||||||
|
|
||||||
|
self.dataStack = dataStack
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
public func didRemoveFromDataStack(dataStack: DataStack) {
|
||||||
|
|
||||||
|
self.dataStack = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MAKR: LocalStorage
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `NSURL` that points to the SQLite file
|
||||||
|
*/
|
||||||
|
public let fileURL: NSURL
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `NSBundle`s from which to search mapping models for migrations
|
||||||
|
*/
|
||||||
|
public let mappingModelBundles: [NSBundle]
|
||||||
|
|
||||||
|
/**
|
||||||
|
Options that tell the `DataStack` how to setup the persistent store
|
||||||
|
*/
|
||||||
|
public var localStorageOptions: LocalStorageOptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
The options dictionary for the specified `LocalStorageOptions`
|
||||||
|
*/
|
||||||
|
public func storeOptionsForOptions(options: LocalStorageOptions) -> [String: AnyObject]? {
|
||||||
|
|
||||||
|
if options == .None {
|
||||||
|
|
||||||
|
return self.storeOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
var storeOptions = self.storeOptions ?? [:]
|
||||||
|
if options.contains(.AllowSynchronousLightweightMigration) {
|
||||||
|
|
||||||
|
storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true
|
||||||
|
storeOptions[NSInferMappingModelAutomaticallyOption] = true
|
||||||
|
}
|
||||||
|
return storeOptions
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
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. For `SQLiteStore`, this converts the database's WAL journaling mode to DELETE before deleting the file.
|
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. For `SQLiteStore`, this converts the database's WAL journaling mode to DELETE before deleting the file.
|
||||||
*/
|
*/
|
||||||
@@ -147,7 +182,7 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Internal
|
||||||
|
|
||||||
internal static let defaultRootDirectory: NSURL = {
|
internal static let defaultRootDirectory: NSURL = {
|
||||||
|
|
||||||
@@ -173,4 +208,9 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore {
|
|||||||
isDirectory: false
|
isDirectory: false
|
||||||
)
|
)
|
||||||
.URLByAppendingPathExtension("sqlite")
|
.URLByAppendingPathExtension("sqlite")
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private weak var dataStack: DataStack?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,19 @@ public protocol StorageInterface: class {
|
|||||||
The options dictionary for the `NSPersistentStore`
|
The options dictionary for the `NSPersistentStore`
|
||||||
*/
|
*/
|
||||||
var storeOptions: [String: AnyObject]? { get }
|
var storeOptions: [String: AnyObject]? { get }
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Internal (Do not call these directly)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
func didAddToDataStack(dataStack: DataStack)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Do not call directly. Used by the `DataStack` internally.
|
||||||
|
*/
|
||||||
|
func didRemoveFromDataStack(dataStack: DataStack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -92,6 +105,7 @@ public struct LocalStorageOptions: OptionSetType, NilLiteralConvertible {
|
|||||||
public static let AllowSynchronousLightweightMigration = LocalStorageOptions(rawValue: 1 << 2)
|
public static let AllowSynchronousLightweightMigration = LocalStorageOptions(rawValue: 1 << 2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: OptionSetType
|
// MARK: OptionSetType
|
||||||
|
|
||||||
public init(rawValue: Int) {
|
public init(rawValue: Int) {
|
||||||
@@ -136,15 +150,17 @@ public protocol LocalStorage: StorageInterface {
|
|||||||
*/
|
*/
|
||||||
var localStorageOptions: LocalStorageOptions { get }
|
var localStorageOptions: LocalStorageOptions { get }
|
||||||
|
|
||||||
|
/**
|
||||||
|
The options dictionary for the specified `LocalStorageOptions`
|
||||||
|
*/
|
||||||
|
func storeOptionsForOptions(options: LocalStorageOptions) -> [String: AnyObject]?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
func eraseStorageAndWait(soureModel soureModel: NSManagedObjectModel) throws
|
func eraseStorageAndWait(soureModel soureModel: NSManagedObjectModel) throws
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Internal
|
|
||||||
|
|
||||||
internal extension LocalStorage {
|
internal extension LocalStorage {
|
||||||
|
|
||||||
internal func matchesPersistentStore(persistentStore: NSPersistentStore) -> Bool {
|
internal func matchesPersistentStore(persistentStore: NSPersistentStore) -> Bool {
|
||||||
@@ -154,3 +170,105 @@ internal extension LocalStorage {
|
|||||||
&& persistentStore.URL == self.fileURL
|
&& persistentStore.URL == self.fileURL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - CloudStorageOptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `CloudStorageOptions` provides settings that tells the `DataStack` how to setup the persistent store for `LocalStorage` implementers.
|
||||||
|
*/
|
||||||
|
public struct CloudStorageOptions: 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 = CloudStorageOptions(rawValue: 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Tells the `DataStack` to delete and recreate the local store from the cloud store on model mismatch, otherwise exceptions will be thrown on failure instead
|
||||||
|
*/
|
||||||
|
public static let RecreateLocalStoreOnModelMismatch = CloudStorageOptions(rawValue: 1 << 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
Tells the `DataStack` to allow lightweight migration for the store when added synchronously
|
||||||
|
*/
|
||||||
|
public static let AllowSynchronousLightweightMigration = CloudStorageOptions(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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - CloudStorage
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `CloudStorage` represents `StorageInterface`s that are synchronized from a cloud-based store.
|
||||||
|
*/
|
||||||
|
public protocol CloudStorage: StorageInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `NSURL` that points to the store file
|
||||||
|
*/
|
||||||
|
var cacheFileURL: NSURL { get }
|
||||||
|
|
||||||
|
/**
|
||||||
|
Options that tell the `DataStack` how to setup the persistent store
|
||||||
|
*/
|
||||||
|
var cloudStorageOptions: CloudStorageOptions { get }
|
||||||
|
|
||||||
|
/**
|
||||||
|
The options dictionary for the specified `CloudStorageOptions`
|
||||||
|
*/
|
||||||
|
func storeOptionsForOptions(options: CloudStorageOptions) -> [String: AnyObject]?
|
||||||
|
|
||||||
|
/**
|
||||||
|
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. (Cloud stores for example, can set the NSPersistentStoreRemoveUbiquitousMetadataOption option before deleting)
|
||||||
|
*/
|
||||||
|
func eraseStorageAndWait(soureModel soureModel: NSManagedObjectModel) throws
|
||||||
|
}
|
||||||
|
|
||||||
|
internal extension CloudStorage {
|
||||||
|
|
||||||
|
internal func matchesPersistentStore(persistentStore: NSPersistentStore) -> Bool {
|
||||||
|
|
||||||
|
guard persistentStore.type == self.dynamicType.storeType
|
||||||
|
&& persistentStore.configurationName == (self.configuration ?? Into.defaultConfigurationName) else {
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard persistentStore.URL == self.cacheFileURL else {
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard let persistentStoreOptions = persistentStore.options,
|
||||||
|
let storeOptions = self.storeOptions else {
|
||||||
|
|
||||||
|
return persistentStore.options == nil && self.storeOptions == nil
|
||||||
|
}
|
||||||
|
return storeOptions.reduce(true) { (isMatch, tuple) in
|
||||||
|
|
||||||
|
let (key, value) = tuple
|
||||||
|
let obj1 = persistentStoreOptions[key] as? NSObject
|
||||||
|
let obj2 = value as? NSObject
|
||||||
|
return isMatch && (obj1 == obj2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user