diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 79e1e7f..51bb760 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -394,6 +394,42 @@ 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 */; }; + B56923C41EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */; }; + B56923C51EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */; }; + B56923C61EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */; }; + B56923C71EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */; }; + B56923C91EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923C81EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift */; }; + B56923CA1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923C81EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift */; }; + B56923CB1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923C81EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift */; }; + B56923CC1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923C81EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift */; }; + B56923E41EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DC1EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift */; }; + B56923E51EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DC1EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift */; }; + B56923E61EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DC1EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift */; }; + B56923E71EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DC1EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift */; }; + B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DD1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift */; }; + B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DD1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift */; }; + B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DD1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift */; }; + B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DD1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift */; }; + B56923EC1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DE1EB827F5007C4DC9 /* SchemaMappingProvider.swift */; }; + B56923ED1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DE1EB827F5007C4DC9 /* SchemaMappingProvider.swift */; }; + B56923EE1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DE1EB827F5007C4DC9 /* SchemaMappingProvider.swift */; }; + B56923EF1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DE1EB827F5007C4DC9 /* SchemaMappingProvider.swift */; }; + B56923F01EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DF1EB827F5007C4DC9 /* XcodeSchemaMappingProvider.swift */; }; + B56923F11EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DF1EB827F5007C4DC9 /* XcodeSchemaMappingProvider.swift */; }; + B56923F21EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DF1EB827F5007C4DC9 /* XcodeSchemaMappingProvider.swift */; }; + B56923F31EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923DF1EB827F5007C4DC9 /* XcodeSchemaMappingProvider.swift */; }; + B56923F51EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923F41EB828BF007C4DC9 /* CSDynamicSchema.swift */; }; + B56923F61EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923F41EB828BF007C4DC9 /* CSDynamicSchema.swift */; }; + B56923F71EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923F41EB828BF007C4DC9 /* CSDynamicSchema.swift */; }; + B56923F81EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923F41EB828BF007C4DC9 /* CSDynamicSchema.swift */; }; + B56923FA1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923F91EB82956007C4DC9 /* CSXcodeDataModelSchema.swift */; }; + B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923F91EB82956007C4DC9 /* CSXcodeDataModelSchema.swift */; }; + B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923F91EB82956007C4DC9 /* CSXcodeDataModelSchema.swift */; }; + B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923F91EB82956007C4DC9 /* CSXcodeDataModelSchema.swift */; }; + B56923FF1EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923FE1EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift */; }; + B56924001EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923FE1EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift */; }; + B56924011EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923FE1EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift */; }; + B56924021EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56923FE1EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.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 */; }; B57D27BE1D0BBE8200539C58 /* BaseTestDataTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57D27BD1D0BBE8200539C58 /* BaseTestDataTestCase.swift */; }; @@ -535,10 +571,6 @@ B5E2222C1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */; }; B5E2222D1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */; }; B5E2222E1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */; }; - B5E41EBB1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */; }; - B5E41EBC1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */; }; - B5E41EBD1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */; }; - B5E41EBE1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */; }; B5E41EC01EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; }; B5E41EC11EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; }; B5E41EC21EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; }; @@ -760,6 +792,15 @@ B565079D1D3930ED000596DA /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; }; B565079F1D3930F5000596DA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; B5677D3C1CD3B1E400322BFC /* ICloudStoreObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ICloudStoreObserver.swift; sourceTree = ""; }; + B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSEntityDescription+Migration.swift"; sourceTree = ""; }; + B56923C81EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectModel+Migration.swift"; sourceTree = ""; }; + B56923DC1EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomSchemaMappingProvider.swift; sourceTree = ""; }; + B56923DD1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InferredSchemaMappingProvider.swift; sourceTree = ""; }; + B56923DE1EB827F5007C4DC9 /* SchemaMappingProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaMappingProvider.swift; sourceTree = ""; }; + B56923DF1EB827F5007C4DC9 /* XcodeSchemaMappingProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XcodeSchemaMappingProvider.swift; sourceTree = ""; }; + B56923F41EB828BF007C4DC9 /* CSDynamicSchema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSDynamicSchema.swift; sourceTree = ""; }; + B56923F91EB82956007C4DC9 /* CSXcodeDataModelSchema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSXcodeDataModelSchema.swift; sourceTree = ""; }; + B56923FE1EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSLegacyXcodeDataModelSchema.swift; sourceTree = ""; }; B56964D31B22FFAD0075EE4A /* DataStack+Migration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = "DataStack+Migration.swift"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; B56965231B356B820075EE4A /* MigrationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationResult.swift; sourceTree = ""; }; B57D27BD1D0BBE8200539C58 /* BaseTestDataTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTestDataTestCase.swift; sourceTree = ""; }; @@ -809,7 +850,6 @@ B5E1B5A71CAA49E2007FD580 /* CSDataStack+Migrating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSDataStack+Migrating.swift"; sourceTree = ""; }; B5E222221CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSynchronousDataTransaction.swift; sourceTree = ""; }; B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSUnsafeDataTransaction.swift; sourceTree = ""; }; - B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationMappingProvider.swift; sourceTree = ""; }; B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DynamicSchema+Convenience.swift"; sourceTree = ""; }; B5E834B81B76311F001D3D50 /* BaseDataTransaction+Importing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BaseDataTransaction+Importing.swift"; sourceTree = ""; }; B5E834BA1B7691F3001D3D50 /* Functions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Functions.swift; sourceTree = ""; }; @@ -1088,6 +1128,7 @@ children = ( B546F9571C99B17400D5AC55 /* CSCoreStore+Setup.swift */, B5DBE2D11C991B3E00B5CEFA /* CSDataStack.swift */, + B56923D71EB8279B007C4DC9 /* Dynamic Models */, B546F9621C9A140E00D5AC55 /* StorageInterfaces */, ); name = Setup; @@ -1136,6 +1177,35 @@ name = Transactions; sourceTree = ""; }; + B56923D71EB8279B007C4DC9 /* Dynamic Models */ = { + isa = PBXGroup; + children = ( + B56923D81EB827B1007C4DC9 /* Dynamic Schema */, + ); + name = "Dynamic Models"; + sourceTree = ""; + }; + B56923D81EB827B1007C4DC9 /* Dynamic Schema */ = { + isa = PBXGroup; + children = ( + B56923F41EB828BF007C4DC9 /* CSDynamicSchema.swift */, + B56923F91EB82956007C4DC9 /* CSXcodeDataModelSchema.swift */, + B56923FE1EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift */, + ); + name = "Dynamic Schema"; + sourceTree = ""; + }; + B56923DB1EB827F5007C4DC9 /* Schema Mapping Providers */ = { + isa = PBXGroup; + children = ( + B56923DE1EB827F5007C4DC9 /* SchemaMappingProvider.swift */, + B56923DD1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift */, + B56923DF1EB827F5007C4DC9 /* XcodeSchemaMappingProvider.swift */, + B56923DC1EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift */, + ); + path = "Schema Mapping Providers"; + sourceTree = ""; + }; B56964D11B22FF700075EE4A /* Migrating */ = { isa = PBXGroup; children = ( @@ -1145,7 +1215,7 @@ B56007151B4018AB00A9A8F9 /* MigrationChain.swift */, B5A261201B64BFDB006EB6D3 /* MigrationType.swift */, B56965231B356B820075EE4A /* MigrationResult.swift */, - B5E41EB91EA8C3A1006240F0 /* Migration Mapping Providers */, + B56923DB1EB827F5007C4DC9 /* Schema Mapping Providers */, ); path = Migrating; sourceTree = ""; @@ -1236,14 +1306,6 @@ name = "Fetching and Querying"; sourceTree = ""; }; - B5E41EB91EA8C3A1006240F0 /* Migration Mapping Providers */ = { - isa = PBXGroup; - children = ( - B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */, - ); - path = "Migration Mapping Providers"; - sourceTree = ""; - }; B5E834B61B7630BD001D3D50 /* Importing */ = { isa = PBXGroup; children = ( @@ -1364,6 +1426,7 @@ B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */, B526613F1CADD585007B85D9 /* CoreStoreFetchRequest+CoreStore.swift */, B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */, + B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */, B51260921E9B28F100402229 /* EntityIdentifier.swift */, B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */, B5E834BA1B7691F3001D3D50 /* Functions.swift */, @@ -1376,6 +1439,7 @@ B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */, B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */, B5E84F331AFF85470064E85B /* NSManagedObjectContext+Transaction.swift */, + B56923C81EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift */, B5FEC18D1C9166E200532541 /* NSPersistentStore+Setup.swift */, B59AFF401C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift */, B5E84F2D1AFF849C0064E85B /* WeakObject.swift */, @@ -1708,6 +1772,7 @@ B5E84F221AFF84860064E85B /* ObjectMonitor.swift in Sources */, B5ECDBF91CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */, B5C976E71C6E3A5A00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */, + B56923F51EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */, B5F1DA901B9AA991007C5CBB /* ImportableUniqueObject.swift in Sources */, B51260891E9B252B00402229 /* NSEntityDescription+DynamicModel.swift in Sources */, B504D0D61B02362500B2BBB1 /* CoreStore+Setup.swift in Sources */, @@ -1716,6 +1781,7 @@ B5E84F131AFF847B0064E85B /* Where.swift in Sources */, B5D339D81E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5D3F6451C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */, + B56923FA1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, B596BBBB1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDBFF1CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC051CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -1746,6 +1812,7 @@ B5E84F0E1AFF847B0064E85B /* Tweak.swift in Sources */, B5E1B5931CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */, B5ECDC291CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */, + B56923F01EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */, B5E84F121AFF847B0064E85B /* OrderBy.swift in Sources */, B546F9581C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */, B5677D3D1CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */, @@ -1775,6 +1842,7 @@ B501FDE21CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, 2F291E2719C6D3CF007AF63F /* CoreStore.swift in Sources */, B5ECDC111CA816E500C7F112 /* CSTweak.swift in Sources */, + B56923C41EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, B5E84F411AFF8CCD0064E85B /* ClauseTypes.swift in Sources */, B5E84F0D1AFF847B0064E85B /* BaseDataTransaction+Querying.swift in Sources */, B52F74451E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */, @@ -1806,8 +1874,11 @@ B5AEFAB51C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, B5E2222A1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */, B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */, + B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B5D33A011E96012400C880DE /* Relationship.swift in Sources */, B5E84EE81AFF84610064E85B /* CoreStoreLogger.swift in Sources */, + B56923C91EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, + B56923E41EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift in Sources */, B58D0C631EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */, B533C4DB1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift in Sources */, B559CD491CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, @@ -1829,8 +1900,9 @@ B5A991EC1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5FE4DA71C84FB4400FA6A91 /* InMemoryStore.swift in Sources */, B52F743D1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, - B5E41EBB1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */, + B56923FF1EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift in Sources */, B5ECDBEC1CA6BF2000C7F112 /* CSFrom.swift in Sources */, + B56923EC1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B5E834B91B76311F001D3D50 /* BaseDataTransaction+Importing.swift in Sources */, B5E84EE61AFF84610064E85B /* DefaultLogger.swift in Sources */, B53FBA041CAB300C00F0D40A /* CSMigrationType.swift in Sources */, @@ -1884,6 +1956,7 @@ 82BA18B61C4BBD3F00A0916E /* DataStack+Querying.swift in Sources */, B5ECDBFB1CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */, B5C976E81C6E3A5D00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */, + B56923F61EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */, 82BA18A21C4BBD1D00A0916E /* CoreStoreError.swift in Sources */, B512608A1E9B252B00402229 /* NSEntityDescription+DynamicModel.swift in Sources */, 82BA18B21C4BBD3900A0916E /* ImportableObject.swift in Sources */, @@ -1892,6 +1965,7 @@ 82BA18AB1C4BBD3100A0916E /* AsynchronousDataTransaction.swift in Sources */, B5D339D91E9489AB00C880DE /* CoreStoreObject.swift in Sources */, 82BA18CE1C4BBD7100A0916E /* FetchedResultsControllerDelegate.swift in Sources */, + B56923FB1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, B596BBBC1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC011CA80CBA00C7F112 /* CSWhere.swift in Sources */, B5ECDC071CA8138100C7F112 /* CSOrderBy.swift in Sources */, @@ -1922,6 +1996,7 @@ 82BA18B31C4BBD3900A0916E /* ImportableUniqueObject.swift in Sources */, B5E1B5951CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */, B5ECDC2B1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */, + B56923F11EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */, 82BA18A11C4BBD1D00A0916E /* CoreStore.swift in Sources */, B546F9591C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */, B5677D3F1CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */, @@ -1951,6 +2026,7 @@ B501FDE41CA8D1F500BE22EF /* CSListMonitor.swift in Sources */, B5FE4DA31C8481E100FA6A91 /* StorageInterface.swift in Sources */, B5ECDC131CA816E500C7F112 /* CSTweak.swift in Sources */, + B56923C51EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, 82BA18C91C4BBD5900A0916E /* MigrationType.swift in Sources */, 82BA18D01C4BBD7100A0916E /* MigrationManager.swift in Sources */, B52F74461E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */, @@ -1982,8 +2058,11 @@ B5E2222C1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */, 82BA18A71C4BBD2900A0916E /* CoreStore+Logging.swift in Sources */, 82BA18D81C4BBD7100A0916E /* WeakObject.swift in Sources */, + B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B5D33A021E96012400C880DE /* Relationship.swift in Sources */, B559CD4B1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, + B56923CA1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, + B56923E51EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift in Sources */, B58D0C641EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */, B533C4DC1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift in Sources */, B5ECDC311CA81CDC00C7F112 /* CSCoreStore+Transaction.swift in Sources */, @@ -2005,8 +2084,9 @@ B5A991ED1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5ECDBEE1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743E1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, - B5E41EBC1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */, + B56924001EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift in Sources */, 82BA18D61C4BBD7100A0916E /* NSManagedObjectContext+Transaction.swift in Sources */, + B56923ED1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, 82BA18B91C4BBD4A00A0916E /* From.swift in Sources */, B53FBA061CAB300C00F0D40A /* CSMigrationType.swift in Sources */, 82BA18BE1C4BBD4A00A0916E /* Tweak.swift in Sources */, @@ -2060,6 +2140,7 @@ B5220E1E1D13080D009BC71E /* CSListMonitor.swift in Sources */, B5DBE2D01C9914A900B5CEFA /* CSCoreStore.swift in Sources */, B5677D411CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */, + B56923F81EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */, B52DD1BE1BE1F94300949AFE /* Progress+Convenience.swift in Sources */, B512608C1E9B252B00402229 /* NSEntityDescription+DynamicModel.swift in Sources */, B5ECDC151CA816E500C7F112 /* CSTweak.swift in Sources */, @@ -2068,6 +2149,7 @@ B5ECDC271CA81A3900C7F112 /* CSCoreStore+Querying.swift in Sources */, B5D339DB1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B52DD1951BE1F92500949AFE /* CoreStoreError.swift in Sources */, + B56923FD1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, B596BBBE1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B546F9601C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */, B5ECDC0F1CA8161B00C7F112 /* CSGroupBy.swift in Sources */, @@ -2098,6 +2180,7 @@ B5220E1F1D130810009BC71E /* CSListObserver.swift in Sources */, B52DD1941BE1F92500949AFE /* CoreStore.swift in Sources */, B52DD1A61BE1F92F00949AFE /* BaseDataTransaction+Importing.swift in Sources */, + B56923F31EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */, B5220E1D1D13080A009BC71E /* CSDataStack+Observing.swift in Sources */, B52DD1A91BE1F93200949AFE /* CoreStore+Querying.swift in Sources */, B5519A4D1CA1F4FB002BEF78 /* CSError.swift in Sources */, @@ -2127,6 +2210,7 @@ B5ECDBE91CA6BEA300C7F112 /* CSClauseTypes.swift in Sources */, B52DD1B81BE1F94000949AFE /* DataStack+Migration.swift in Sources */, B5ECDC091CA8138100C7F112 /* CSOrderBy.swift in Sources */, + B56923C71EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, B52DD1A51BE1F92F00949AFE /* ImportableUniqueObject.swift in Sources */, B5E222271CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */, B52F74481E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */, @@ -2158,8 +2242,11 @@ B52DD19A1BE1F92800949AFE /* CoreStore+Logging.swift in Sources */, B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */, B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */, + B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B5D33A041E96012400C880DE /* Relationship.swift in Sources */, B52DD1C61BE1F94600949AFE /* NSManagedObjectContext+CoreStore.swift in Sources */, + B56923CC1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, + B56923E71EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift in Sources */, B58D0C661EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */, B533C4DE1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift in Sources */, B5220E141D130614009BC71E /* DataStack+Observing.swift in Sources */, @@ -2181,8 +2268,9 @@ B5A991EF1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5220E201D130813009BC71E /* CSObjectMonitor.swift in Sources */, B52F74401E9B8724005F3DAC /* DynamicSchema.swift in Sources */, - B5E41EBE1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */, + B56924021EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift in Sources */, B5220E171D1306DF009BC71E /* UnsafeDataTransaction+Observing.swift in Sources */, + B56923EF1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B53FBA081CAB300C00F0D40A /* CSMigrationType.swift in Sources */, B52DD1B91BE1F94000949AFE /* CoreStore+Migration.swift in Sources */, B5519A5C1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */, @@ -2236,6 +2324,7 @@ B56321A91BD65219006C9394 /* Progress+Convenience.swift in Sources */, B5ECDBFC1CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */, B5C976E91C6E3A5E00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */, + B56923F71EB828BF007C4DC9 /* CSDynamicSchema.swift in Sources */, B56321801BD65216006C9394 /* CoreStoreError.swift in Sources */, B512608B1E9B252B00402229 /* NSEntityDescription+DynamicModel.swift in Sources */, B56321AD1BD6521C006C9394 /* MigrationManager.swift in Sources */, @@ -2244,6 +2333,7 @@ B56321961BD65216006C9394 /* From.swift in Sources */, B5D339DA1E9489AB00C880DE /* CoreStoreObject.swift in Sources */, B5ECDC021CA80CBA00C7F112 /* CSWhere.swift in Sources */, + B56923FC1EB82956007C4DC9 /* CSXcodeDataModelSchema.swift in Sources */, B596BBBD1DD5C39F001DCDD9 /* QueryableSource.swift in Sources */, B5ECDC081CA8138100C7F112 /* CSOrderBy.swift in Sources */, B5E1B59B1CAA0C23007FD580 /* CSObjectObserver.swift in Sources */, @@ -2274,6 +2364,7 @@ B5E1B5961CAA0C15007FD580 /* CSObjectMonitor.swift in Sources */, B5ECDC2C1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */, B56321911BD65216006C9394 /* BaseDataTransaction+Importing.swift in Sources */, + B56923F21EB827F6007C4DC9 /* XcodeSchemaMappingProvider.swift in Sources */, B546F95A1C99B17400D5AC55 /* CSCoreStore+Setup.swift in Sources */, B5677D401CD3B1E400322BFC /* ICloudStoreObserver.swift in Sources */, B56321941BD65216006C9394 /* CoreStore+Querying.swift in Sources */, @@ -2303,6 +2394,7 @@ B5ECDC141CA816E500C7F112 /* CSTweak.swift in Sources */, B56321AE1BD6521C006C9394 /* NotificationObserver.swift in Sources */, B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */, + B56923C61EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */, B56321A71BD65216006C9394 /* MigrationResult.swift in Sources */, B598514A1C90289E00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */, B52F74471E9B8724005F3DAC /* XcodeDataModelSchema.swift in Sources */, @@ -2334,8 +2426,11 @@ B5E2222D1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */, B563219F1BD65216006C9394 /* ObjectMonitor.swift in Sources */, B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */, + B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */, B5D33A031E96012400C880DE /* Relationship.swift in Sources */, B559CD4C1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */, + B56923CB1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */, + B56923E61EB827F5007C4DC9 /* CustomSchemaMappingProvider.swift in Sources */, B58D0C651EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */, B533C4DD1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift in Sources */, B5ECDC321CA81CDC00C7F112 /* CSCoreStore+Transaction.swift in Sources */, @@ -2357,8 +2452,9 @@ B5A991EE1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */, B5ECDBEF1CA6BF2000C7F112 /* CSFrom.swift in Sources */, B52F743F1E9B8724005F3DAC /* DynamicSchema.swift in Sources */, - B5E41EBD1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */, + B56924011EB82976007C4DC9 /* CSLegacyXcodeDataModelSchema.swift in Sources */, B56321B41BD6521C006C9394 /* NSManagedObjectContext+Transaction.swift in Sources */, + B56923EE1EB827F6007C4DC9 /* SchemaMappingProvider.swift in Sources */, B56321861BD65216006C9394 /* CoreStoreLogger.swift in Sources */, B53FBA071CAB300C00F0D40A /* CSMigrationType.swift in Sources */, B56321841BD65216006C9394 /* DefaultLogger.swift in Sources */, diff --git a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj index 7e80f73..7a30696 100644 --- a/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj +++ b/CoreStoreDemo/CoreStoreDemo.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ B503FAE11AFDC71700F90881 /* Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADD1AFDC71700F90881 /* Palette.swift */; }; B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */; }; B5125C121B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B5125C111B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel */; }; - B5125C141B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B5125C131B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel */; }; B52977D91B120B80003D50A5 /* ObserversViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977D81B120B80003D50A5 /* ObserversViewController.swift */; }; B52977DD1B120F3B003D50A5 /* TransactionsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */; }; B52977DF1B120F83003D50A5 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B52977DE1B120F83003D50A5 /* MapKit.framework */; }; @@ -26,6 +25,7 @@ B560070F1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B560070E1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift */; }; B566E32A1B117B1F00F4F0C6 /* StackSetupDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566E3291B117B1F00F4F0C6 /* StackSetupDemoViewController.swift */; }; B566E3321B11DF3200F4F0C6 /* UserAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = B566E3311B11DF3200F4F0C6 /* UserAccount.swift */; }; + B56924091EBAE435007C4DC9 /* OrganismV3ToV2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B5125C131B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel */; }; B56964C91B20AC780075EE4A /* CustomLoggerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56964C81B20AC780075EE4A /* CustomLoggerViewController.swift */; }; B56964D71B231AE90075EE4A /* StackSetupDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B56964D51B231AE90075EE4A /* StackSetupDemo.xcdatamodeld */; }; B56964DA1B231BCA0075EE4A /* MaleAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56964D91B231BCA0075EE4A /* MaleAccount.swift */; }; @@ -330,8 +330,8 @@ B5EE259B1B3EA4890000406B /* OrganismV3.swift in Sources */, B503FAE11AFDC71700F90881 /* Palette.swift in Sources */, B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */, + B56924091EBAE435007C4DC9 /* OrganismV3ToV2.xcmappingmodel in Sources */, B560070F1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift in Sources */, - B5125C141B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel in Sources */, B503FADF1AFDC71700F90881 /* ListObserverDemoViewController.swift in Sources */, B54AAD4F1AF4D26E00848AE0 /* AppDelegate.swift in Sources */, B56964D71B231AE90075EE4A /* StackSetupDemo.xcdatamodeld in Sources */, diff --git a/CoreStoreDemo/CoreStoreDemo/AppDelegate.swift b/CoreStoreDemo/CoreStoreDemo/AppDelegate.swift index 59d7571..3b8a8c4 100644 --- a/CoreStoreDemo/CoreStoreDemo/AppDelegate.swift +++ b/CoreStoreDemo/CoreStoreDemo/AppDelegate.swift @@ -19,51 +19,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool { application.statusBarStyle = .lightContent - - /// Generated by CoreStore on 4/21/17, 2:41 PM - class Place: CoreStoreObject { - - let latitude = Value.Optional("latitude", default: 0.0) - let title = Value.Optional("title") - let longitude = Value.Optional("longitude", default: 0.0) - let subtitle = Value.Optional("subtitle") - } - class Palette: CoreStoreObject { - - let saturation = Value.Optional("saturation", default: 0.0) - let hue = Value.Optional("hue", default: 0) - let brightness = Value.Optional("brightness", default: 0.0) - let colorName = Value.Optional("colorName", isTransient: true) - } - class TimeZone: CoreStoreObject { - - let secondsFromGMT = Value.Optional("secondsFromGMT", default: 0) - let name = Value.Optional("name") - let daylightSavingTimeOffset = Value.Optional("daylightSavingTimeOffset", default: 0.0) - let abbreviation = Value.Optional("abbreviation") - let hasDaylightSavingTime = Value.Optional("hasDaylightSavingTime") - } - - - - let schema = CoreStoreSchema( - modelVersion: "CoreStoreDemo", - entities: [ - Entity("Place"), - Entity("Palette"), - Entity("TimeZone"), - ], - versionLock: [ - "Place": [0x25cb5bd001887b92, 0xfe86dd433a5e0430, 0xcca50ac3f3659b68, 0xfe4e494ff66439b0], - "Palette": [0xa306515d026d3c43, 0x1b299716733e56f6, 0x53bff8954221a1b6, 0xa74d6b1e613923ab], - "TimeZone": [0x92e08db969e46163, 0xae9cf1ab738868c5, 0xb6a269249771a562, 0x58a357eab4c99ed5] - ] - ) - - print(schema.printCoreStoreSchema()) return true } } diff --git a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift index b73bcac..3299eae 100644 --- a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/MigrationsDemoViewController.swift @@ -227,7 +227,12 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD self.setEnabled(false) let progress = dataStack.addStorage( - SQLiteStore(fileName: "MigrationDemo.sqlite"), + SQLiteStore( + fileName: "MigrationDemo.sqlite"/*, + migrationMappingProviders: [ + CustomSchemaMappingProvider(from: "MigrationDemoV3", to: "MigrationDemoV2") + ]*/ + ), completion: { [weak self] (result) -> Void in guard let `self` = self else { diff --git a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/OrganismV3ToV2.xcmappingmodel/xcmapping.xml b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/OrganismV3ToV2.xcmappingmodel/xcmapping.xml index 30118b0..2798a07 100644 --- a/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/OrganismV3ToV2.xcmappingmodel/xcmapping.xml +++ b/CoreStoreDemo/CoreStoreDemo/MIgrations Demo/OrganismV3ToV2.xcmappingmodel/xcmapping.xml @@ -1,4 +1,4 @@ - + @@ -10,7 +10,7 @@ NSPersistenceFrameworkVersion - 526 + 754 NSStoreModelVersionHashes XDDevAttributeMapping diff --git a/CoreStoreDemo/CoreStoreDemo/MigrationDemo.xcdatamodeld/MigrationDemoV3.xcdatamodel/contents b/CoreStoreDemo/CoreStoreDemo/MigrationDemo.xcdatamodeld/MigrationDemoV3.xcdatamodel/contents index 04a1049..61c02bc 100644 --- a/CoreStoreDemo/CoreStoreDemo/MigrationDemo.xcdatamodeld/MigrationDemoV3.xcdatamodel/contents +++ b/CoreStoreDemo/CoreStoreDemo/MigrationDemo.xcdatamodeld/MigrationDemoV3.xcdatamodel/contents @@ -1,11 +1,11 @@ - + - + diff --git a/CoreStoreTests/SetupTests.swift b/CoreStoreTests/SetupTests.swift index 2a7ee7d..adc27a9 100644 --- a/CoreStoreTests/SetupTests.swift +++ b/CoreStoreTests/SetupTests.swift @@ -263,7 +263,7 @@ class SetupTests: BaseTestDataTestCase { ) do { - let sqliteStore = LegacySQLiteStore() + let sqliteStore = SQLiteStore.legacy() do { try stack.addStorageAndWait(sqliteStore) @@ -278,7 +278,7 @@ class SetupTests: BaseTestDataTestCase { } do { - let sqliteStore = LegacySQLiteStore( + let sqliteStore = SQLiteStore.legacy( fileName: "ConfigStore1.sqlite", configuration: "Config1", localStorageOptions: .recreateStoreOnModelMismatch @@ -297,7 +297,7 @@ class SetupTests: BaseTestDataTestCase { } do { - let sqliteStore = LegacySQLiteStore( + let sqliteStore = SQLiteStore.legacy( fileName: "ConfigStore2.sqlite", configuration: "Config2", localStorageOptions: .recreateStoreOnModelMismatch @@ -320,7 +320,7 @@ class SetupTests: BaseTestDataTestCase { dynamic func test_ThatLegacySQLiteStores_DeleteFilesCorrectly() { let fileManager = FileManager.default - let sqliteStore = LegacySQLiteStore() + let sqliteStore = SQLiteStore.legacy() func createStore() throws -> [String: Any] { do { diff --git a/CoreStoreTests/StorageInterfaceTests.swift b/CoreStoreTests/StorageInterfaceTests.swift index 9c85fd6..23f9615 100644 --- a/CoreStoreTests/StorageInterfaceTests.swift +++ b/CoreStoreTests/StorageInterfaceTests.swift @@ -86,7 +86,7 @@ final class StorageInterfaceTests: XCTestCase { XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary) XCTAssertEqual(store.fileURL, SQLiteStore.defaultFileURL) - XCTAssertEqual(store.mappingModelBundles, Bundle.allBundles) + XCTAssertTrue(store.migrationMappingProviders.isEmpty) XCTAssertEqual(store.localStorageOptions, .none) } @@ -96,12 +96,15 @@ final class StorageInterfaceTests: XCTestCase { let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()) .appendingPathComponent(NSUUID().uuidString, isDirectory: false)! .appendingPathExtension("db") - let bundles = [Bundle(for: type(of: self))] + let mappingProvider = XcodeSchemaMappingProvider( + from: "V1", to: "V2", + mappingModelBundle: Bundle(for: type(of: self)) + ) let store = SQLiteStore( fileURL: fileURL, configuration: "config1", - mappingModelBundles: bundles, + migrationMappingProviders: [mappingProvider], localStorageOptions: .recreateStoreOnModelMismatch ) XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType) @@ -109,7 +112,7 @@ final class StorageInterfaceTests: XCTestCase { XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary) XCTAssertEqual(store.fileURL, fileURL) - XCTAssertEqual(store.mappingModelBundles, bundles) + XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider]) XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch]) } @@ -117,12 +120,14 @@ final class StorageInterfaceTests: XCTestCase { dynamic func test_ThatFileNameSQLiteStores_ConfigureCorrectly() { let fileName = UUID().uuidString + ".db" - let bundles = [Bundle(for: type(of: self))] - + let mappingProvider = XcodeSchemaMappingProvider( + from: "V1", to: "V2", + mappingModelBundle: Bundle(for: type(of: self)) + ) let store = SQLiteStore( fileName: fileName, configuration: "config1", - mappingModelBundles: bundles, + migrationMappingProviders: [mappingProvider], localStorageOptions: .recreateStoreOnModelMismatch ) XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType) @@ -131,7 +136,7 @@ final class StorageInterfaceTests: XCTestCase { XCTAssertEqual(store.fileURL.deletingLastPathComponent(), SQLiteStore.defaultRootDirectory) XCTAssertEqual(store.fileURL.lastPathComponent, fileName) - XCTAssertEqual(store.mappingModelBundles, bundles) + XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider]) XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch]) } @@ -152,65 +157,44 @@ final class StorageInterfaceTests: XCTestCase { .appendingPathComponent(DataStack.applicationName, isDirectory: false) .appendingPathExtension("sqlite") - XCTAssertEqual(LegacySQLiteStore.defaultRootDirectory, legacyDefaultRootDirectory) - XCTAssertEqual(LegacySQLiteStore.defaultFileURL, legacyDefaultFileURL) + XCTAssertEqual(SQLiteStore.legacyDefaultRootDirectory, legacyDefaultRootDirectory) + XCTAssertEqual(SQLiteStore.legacyDefaultFileURL, legacyDefaultFileURL) } @objc dynamic func test_ThatDefaultLegacySQLiteStores_ConfigureCorrectly() { - let store = LegacySQLiteStore() + let store = SQLiteStore.legacy() XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType) XCTAssertNil(store.configuration) XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary) - XCTAssertEqual(store.fileURL, LegacySQLiteStore.defaultFileURL) - XCTAssertEqual(store.mappingModelBundles, Bundle.allBundles) + XCTAssertEqual(store.fileURL, SQLiteStore.legacyDefaultFileURL) + XCTAssertTrue(store.migrationMappingProviders.isEmpty) XCTAssertEqual(store.localStorageOptions, .none) } - @objc - dynamic func test_ThatFileURLLegacySQLiteStores_ConfigureCorrectly() { - - let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()) - .appendingPathComponent(NSUUID().uuidString, isDirectory: false)! - .appendingPathExtension("db") - let bundles = [Bundle(for: type(of: self))] - - let store = LegacySQLiteStore( - fileURL: fileURL, - configuration: "config1", - mappingModelBundles: bundles, - localStorageOptions: .recreateStoreOnModelMismatch - ) - XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType) - XCTAssertEqual(store.configuration, "config1") - XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary) - - XCTAssertEqual(store.fileURL, fileURL) - XCTAssertEqual(store.mappingModelBundles, bundles) - XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch]) - } - @objc dynamic func test_ThatFileNameLegacySQLiteStores_ConfigureCorrectly() { let fileName = UUID().uuidString + ".db" - let bundles = [Bundle(for: type(of: self))] - - let store = LegacySQLiteStore( + let mappingProvider = XcodeSchemaMappingProvider( + from: "V1", to: "V2", + mappingModelBundle: Bundle(for: type(of: self)) + ) + let store = SQLiteStore.legacy( fileName: fileName, configuration: "config1", - mappingModelBundles: bundles, + migrationMappingProviders: [mappingProvider], localStorageOptions: .recreateStoreOnModelMismatch ) XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType) XCTAssertEqual(store.configuration, "config1") XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary) - XCTAssertEqual(store.fileURL.deletingLastPathComponent(), LegacySQLiteStore.defaultRootDirectory) + XCTAssertEqual(store.fileURL.deletingLastPathComponent(), SQLiteStore.legacyDefaultRootDirectory) XCTAssertEqual(store.fileURL.lastPathComponent, fileName) - XCTAssertEqual(store.mappingModelBundles, bundles) + XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider]) XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch]) } } diff --git a/README.md b/README.md index 7e67bec..3e6cbba 100644 --- a/README.md +++ b/README.md @@ -270,10 +270,6 @@ try CoreStore.addStorageAndWait( ) ) ``` -`InMemoryStore`s also implement the `DefaultInitializableStore` sugar protocol which tells CoreStore that this store can initialize without any arguments (`init()`). This lets us provide just the type instead of an instance: -```swift -try CoreStore.addStorageAndWait(InMemoryStore.self) -``` ### Local Store The most common `StorageInterface` you will probably use is the `SQLiteStore`, which saves data in a local SQLite file. @@ -290,9 +286,9 @@ let migrationProgress = CoreStore.addStorage( ``` Refer to the *SQLiteStore.swift* source documentation for detailed explanations for each of the default values. -CoreStore can decide the default values for these properties, so `SQLiteStore`s also implement the `DefaultInitializableStore` sugar protocol which lets us write: +CoreStore can decide the default values for these properties, so `SQLiteStore`s can be initialized with default arguments: ```swift -try CoreStore.addStorageAndWait(SQLiteStore.self) +try CoreStore.addStorageAndWait(SQLiteStore()) ``` or ```swift diff --git a/Sources/Internal/EntityIdentifier.swift b/Sources/Internal/EntityIdentifier.swift index 6004139..ad560cd 100644 --- a/Sources/Internal/EntityIdentifier.swift +++ b/Sources/Internal/EntityIdentifier.swift @@ -74,7 +74,7 @@ internal struct EntityIdentifier: Hashable { internal init(_ entityDescription: NSEntityDescription) { - if let anyEntity = entityDescription.anyEntity { + if let anyEntity = entityDescription.coreStoreEntity { self.category = .coreStore self.interfacedClassName = NSStringFromClass(anyEntity.type) diff --git a/Sources/Internal/NSEntityDescription+DynamicModel.swift b/Sources/Internal/NSEntityDescription+DynamicModel.swift index bfce9af..46094c9 100644 --- a/Sources/Internal/NSEntityDescription+DynamicModel.swift +++ b/Sources/Internal/NSEntityDescription+DynamicModel.swift @@ -32,7 +32,7 @@ import Foundation internal extension NSEntityDescription { @nonobjc - internal var anyEntity: CoreStoreSchema.AnyEntity? { + internal var coreStoreEntity: CoreStoreSchema.AnyEntity? { get { diff --git a/Sources/Internal/NSEntityDescription+Migration.swift b/Sources/Internal/NSEntityDescription+Migration.swift new file mode 100644 index 0000000..8918530 --- /dev/null +++ b/Sources/Internal/NSEntityDescription+Migration.swift @@ -0,0 +1,55 @@ +// +// NSEntityDescription+Migration.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - NSEntityDescription + +internal extension NSEntityDescription { + + @nonobjc + internal func cs_resolvedAttributeRenamingIdentities() -> [String: (attribute: NSAttributeDescription, versionHash: Data)] { + + var mapping: [String: (attribute: NSAttributeDescription, versionHash: Data)] = [:] + for (attributeName, attributeDescription) in self.attributesByName { + + mapping[attributeDescription.renamingIdentifier ?? attributeName] = (attributeDescription, attributeDescription.versionHash) + } + return mapping + } + + @nonobjc + internal func cs_resolvedRelationshipRenamingIdentities() -> [String: (relationship: NSRelationshipDescription, versionHash: Data)] { + + var mapping: [String: (relationship: NSRelationshipDescription, versionHash: Data)] = [:] + for (relationshipName, relationshipDescription) in self.relationshipsByName { + + mapping[relationshipDescription.renamingIdentifier ?? relationshipName] = (relationshipDescription, relationshipDescription.versionHash) + } + return mapping + } +} diff --git a/Sources/Internal/NSManagedObjectModel+Migration.swift b/Sources/Internal/NSManagedObjectModel+Migration.swift new file mode 100644 index 0000000..f58914b --- /dev/null +++ b/Sources/Internal/NSManagedObjectModel+Migration.swift @@ -0,0 +1,44 @@ +// +// NSManagedObjectModel+Migration.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - FilePrivate + +internal extension NSManagedObjectModel { + + @nonobjc + internal func cs_resolvedRenamingIdentities() -> [String: (entity: NSEntityDescription, versionHash: Data)] { + + var mapping: [String: (entity: NSEntityDescription, versionHash: Data)] = [:] + for (entityName, entityDescription) in self.entitiesByName { + + mapping[entityDescription.renamingIdentifier ?? entityName] = (entityDescription, entityDescription.versionHash) + } + return mapping + } +} diff --git a/Sources/Logging/CoreStore+CustomDebugStringConvertible.swift b/Sources/Logging/CoreStore+CustomDebugStringConvertible.swift index 0fbb2c5..92adf7a 100644 --- a/Sources/Logging/CoreStore+CustomDebugStringConvertible.swift +++ b/Sources/Logging/CoreStore+CustomDebugStringConvertible.swift @@ -372,34 +372,6 @@ extension Into: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { } -// MARK: - LegacySQLiteStore - -extension LegacySQLiteStore: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { - - // MARK: CustomDebugStringConvertible - - public var debugDescription: String { - - return formattedDebugDescription(self) - } - - - // MARK: CoreStoreDebugStringConvertible - - public var coreStoreDumpString: String { - - return createFormattedString( - "(", ")", - ("configuration", self.configuration as Any), - ("storeOptions", self.storeOptions as Any), - ("fileURL", self.fileURL), - ("mappingModelBundles", self.mappingModelBundles), - ("localStorageOptions", self.localStorageOptions) - ) - } -} - - // MARK: - LegacyXcodeDataModelSchema extension LegacyXcodeDataModelSchema: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { @@ -893,7 +865,7 @@ extension SQLiteStore: CustomDebugStringConvertible, CoreStoreDebugStringConvert ("configuration", self.configuration as Any), ("storeOptions", self.storeOptions as Any), ("fileURL", self.fileURL), - ("mappingModelBundles", self.mappingModelBundles), + ("migrationMappingProviders", self.migrationMappingProviders), ("localStorageOptions", self.localStorageOptions) ) } diff --git a/Sources/Logging/CoreStore+Logging.swift b/Sources/Logging/CoreStore+Logging.swift index c223b77..f74b709 100644 --- a/Sources/Logging/CoreStore+Logging.swift +++ b/Sources/Logging/CoreStore+Logging.swift @@ -63,7 +63,7 @@ public extension CoreStore { } @inline(__always) - internal static func assert( _ condition: @autoclosure () -> Bool, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) { + internal static func assert( _ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) { self.logger.assert( condition, diff --git a/Sources/Migrating/CoreStore+Migration.swift b/Sources/Migrating/CoreStore+Migration.swift index 54ed936..8f16239 100644 --- a/Sources/Migrating/CoreStore+Migration.swift +++ b/Sources/Migrating/CoreStore+Migration.swift @@ -30,27 +30,6 @@ import CoreData // MARK: - CoreStore public extension CoreStore { - - /** - Asynchronously adds a `StorageInterface` with default settings to the `defaultStack`. Migrations are also initiated by default. - ``` - CoreStore.addStorage( - InMemoryStore.self, - completion: { result in - switch result { - case .success(let storage): // ... - case .failure(let error): // ... - } - } - ) - ``` - - parameter storeType: the storage type - - 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. Note that the `StorageInterface` associated to the `SetupResult.success` may not always be the same instance as the parameter argument if a previous `StorageInterface` was already added at the same URL and with the same configuration. - */ - public static func addStorage(_ storeType: T.Type, completion: @escaping (SetupResult) -> Void) where T: DefaultInitializableStore { - - self.defaultStack.addStorage(storeType.init(), completion: completion) - } /** Asynchronously adds a `StorageInterface` to the `defaultStack`. Migrations are also initiated by default. @@ -72,28 +51,6 @@ public extension CoreStore { self.defaultStack.addStorage(storage, completion: completion) } - - /** - Asynchronously adds a `LocalStorage` with default settings to the `defaultStack`. Migrations are also initiated by default. - ``` - let migrationProgress = CoreStore.addStorage( - SQLiteStore.self, - completion: { result in - switch result { - case .success(let storage): // ... - case .failure(let error): // ... - } - } - ) - ``` - - parameter storeType: the local storage type - - 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. 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: a `Progress` instance if a migration has started, or `nil` if either no migrations are required or if a failure occured. - */ - public static func addStorage(_ storeType: T.Type, completion: @escaping (SetupResult) -> Void) -> Progress? where T: DefaultInitializableStore { - - return self.defaultStack.addStorage(storeType.init(), completion: completion) - } /** Asynchronously adds a `LocalStorage` to the `defaultStack`. Migrations are also initiated by default. diff --git a/Sources/Migrating/DataStack+Migration.swift b/Sources/Migrating/DataStack+Migration.swift index 56cc107..fc38d47 100644 --- a/Sources/Migrating/DataStack+Migration.swift +++ b/Sources/Migrating/DataStack+Migration.swift @@ -31,27 +31,6 @@ import CoreData public extension DataStack { - /** - Asynchronously adds a `StorageInterface` with default settings to the stack. Migrations are also initiated by default. - ``` - dataStack.addStorage( - InMemoryStore.self, - completion: { result in - switch result { - case .success(let storage): // ... - case .failure(let error): // ... - } - } - ) - ``` - - parameter storeType: the storage type - - 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. Note that the `StorageInterface` associated to the `SetupResult.success` may not always be the same instance as the parameter argument if a previous `StorageInterface` was already added at the same URL and with the same configuration. - */ - public func addStorage(_ storeType: T.Type, completion: @escaping (SetupResult) -> Void) where T: DefaultInitializableStore { - - self.addStorage(storeType.init(), completion: completion) - } - /** Asynchronously adds a `StorageInterface` to the stack. Migrations are also initiated by default. ``` @@ -109,28 +88,6 @@ public extension DataStack { } } - /** - Asynchronously adds a `LocalStorage` with default settings to the stack. Migrations are also initiated by default. - ``` - let migrationProgress = dataStack.addStorage( - SQLiteStore.self, - completion: { result in - switch result { - case .success(let storage): // ... - case .failure(let error): // ... - } - } - ) - ``` - - parameter storeType: the local storage type - - 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. 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: a `Progress` instance if a migration has started, or `nil` if either no migrations are required or if a failure occured. - */ - public func addStorage(_ storeType: T.Type, completion: @escaping (SetupResult) -> Void) -> Progress? where T: DefaultInitializableStore { - - return self.addStorage(storeType.init() as! T.Type, completion: completion) - } - /** Asynchronously adds a `LocalStorage` to the stack. Migrations are also initiated by default. ``` @@ -683,52 +640,33 @@ public extension DataStack { var migrationSteps = [(sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel, mappingModel: NSMappingModel, migrationType: MigrationType)]() while let nextVersion = migrationChain.nextVersionFrom(currentVersion), - let sourceModel = schemaHistory.rawModel(for: currentVersion), - sourceModel != schemaHistory.rawModel, - let destinationModel = schemaHistory.rawModel(for: nextVersion) { + let sourceSchema = schemaHistory.schema(for: currentVersion), + sourceSchema.modelVersion != schemaHistory.currentModelVersion, + let destinationSchema = schemaHistory.schema(for: nextVersion) { - if let mappingModel = NSMappingModel( - from: storage.mappingModelBundles, - forSourceModel: sourceModel, - destinationModel: destinationModel) { + let mappingProviders = storage.migrationMappingProviders + do { - migrationSteps.append( - ( - sourceModel: sourceModel, - destinationModel: destinationModel, - mappingModel: mappingModel, - migrationType: .heavyweight( - sourceVersion: currentVersion, - destinationVersion: nextVersion - ) - ) - ) - } - else { - - do { + try withExtendedLifetime((sourceSchema.rawModel(), destinationSchema.rawModel())) { (sourceModel, destinationModel) in - let mappingModel = try NSMappingModel.inferredMappingModel( - forSourceModel: sourceModel, - destinationModel: destinationModel + let mapping = try mappingProviders.findMapping( + sourceSchema: sourceSchema, + destinationSchema: destinationSchema, + storage: storage ) - migrationSteps.append( ( sourceModel: sourceModel, destinationModel: destinationModel, - mappingModel: mappingModel, - migrationType: .lightweight( - sourceVersion: currentVersion, - destinationVersion: nextVersion - ) + mappingModel: mapping.mappingModel, + migrationType: mapping.migrationType ) ) } - catch { - - return nil - } + } + catch { + + return nil } currentVersion = nextVersion } @@ -804,3 +742,31 @@ public extension DataStack { } } } + + +// MARK: - FilePrivate + +fileprivate extension Array where Element == SchemaMappingProvider { + + func findMapping(sourceSchema: DynamicSchema, destinationSchema: DynamicSchema, storage: LocalStorage) throws -> (mappingModel: NSMappingModel, migrationType: MigrationType) { + + for element in self { + + switch element { + + case let element as CustomSchemaMappingProvider + where element.sourceVersion == sourceSchema.modelVersion && element.destinationVersion == destinationSchema.modelVersion: + return try element.createMappingModel(from: sourceSchema, to: destinationSchema, storage: storage) + + case let element as XcodeSchemaMappingProvider + where element.sourceVersion == sourceSchema.modelVersion && element.destinationVersion == destinationSchema.modelVersion: + return try element.createMappingModel(from: sourceSchema, to: destinationSchema, storage: storage) + + default: + continue + } + } + return try InferredSchemaMappingProvider() + .createMappingModel(from: sourceSchema, to: destinationSchema, storage: storage) + } +} diff --git a/Sources/Migrating/Migration Mapping Providers/MigrationMappingProvider.swift b/Sources/Migrating/Migration Mapping Providers/MigrationMappingProvider.swift deleted file mode 100644 index cb84183..0000000 --- a/Sources/Migrating/Migration Mapping Providers/MigrationMappingProvider.swift +++ /dev/null @@ -1,160 +0,0 @@ -// -// MigrationMappingProvider.swift -// CoreStore -// -// Copyright © 2017 John Rommel Estropia -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -import CoreData -import Foundation - - -// MARK: - SchemaMappingProvider - -public protocol SchemaMappingProvider { - - var sourceSchema: DynamicSchema { get } - var destinationSchema: DynamicSchema { get } - - func createMappingModel() throws -> (mappingModel: NSMappingModel, migrationType: MigrationType) -} - - -// MARK: - EntityMappingProvider - -public protocol EntityMappingProvider { - - var source: (schema: DynamicSchema, entity: DynamicEntity) { get } - var destination: (schema: DynamicSchema, entity: DynamicEntity) { get } - - func createEntityMapping() -> NSEntityMapping -} - - -// MARK: - LightweightMappingModelProvider - -open class LightweightMappingModelProvider: SchemaMappingProvider { - - public required init(source: SourceSchema, destination: DestinationSchema) { - - self.sourceSchema = source - self.destinationSchema = destination - } - - - // MARK: SchemaMappingProvider - - public let sourceSchema: DynamicSchema - public let destinationSchema: DynamicSchema - - public func createMappingModel() throws -> (mappingModel: NSMappingModel, migrationType: MigrationType) { - - let sourceModel = self.sourceSchema.rawModel() - let destinationModel = self.destinationSchema.rawModel() - - let mappingModel = try NSMappingModel.inferredMappingModel( - forSourceModel: sourceModel, - destinationModel: destinationModel - ) - return ( - mappingModel, - .lightweight( - sourceVersion: self.sourceSchema.modelVersion, - destinationVersion: self.destinationSchema.modelVersion - ) - ) - } -} - - -// MARK: - XcodeMappingModelProvider - -open class XcodeMappingModelProvider: LightweightMappingModelProvider { - - private let mappingModelBundles: [Bundle] - - public required init(source: SourceSchema, destination: DestinationSchema, mappingModelBundles: [Bundle]) { - - self.mappingModelBundles = mappingModelBundles - super.init(source: source, destination: destination) - } - - public required init(source: SourceSchema, destination: DestinationSchema) { - - self.mappingModelBundles = Bundle.allBundles - super.init(source: source, destination: destination) - } - - - // MARK: SchemaMappingProvider - - public override func createMappingModel() throws -> (mappingModel: NSMappingModel, migrationType: MigrationType) { - - let sourceModel = self.sourceSchema.rawModel() - let destinationModel = self.destinationSchema.rawModel() - - if let mappingModel = NSMappingModel( - from: self.mappingModelBundles, - forSourceModel: sourceModel, - destinationModel: destinationModel) { - - return ( - mappingModel, - .heavyweight( - sourceVersion: self.sourceSchema.modelVersion, - destinationVersion: self.destinationSchema.modelVersion - ) - ) - } - return try super.createMappingModel() - } -} - - -// MARK: - UnsafeMigrationProxyObject - -public final class UnsafeMigrationProxyObject { - - public subscript(kvcKey: KeyPath) -> Any? { - - get { - - return self.rawObject.cs_accessValueForKVCKey(kvcKey) - } - set { - - self.rawObject.cs_setValue(newValue, forKVCKey: kvcKey) - } - } - - - // MARK: Internal - - internal init(_ rawObject: NSManagedObject) { - - self.rawObject = rawObject - } - - - // MARK: Private - - private let rawObject: NSManagedObject -} diff --git a/Sources/Migrating/Schema Mapping Providers/CustomSchemaMappingProvider.swift b/Sources/Migrating/Schema Mapping Providers/CustomSchemaMappingProvider.swift new file mode 100644 index 0000000..ced7354 --- /dev/null +++ b/Sources/Migrating/Schema Mapping Providers/CustomSchemaMappingProvider.swift @@ -0,0 +1,665 @@ +// +// CustomSchemaMappingProvider.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - CustomSchemaMappingProvider + +open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider { + + // MARK: - CustomMapping + + public enum CustomMapping: Hashable { + + case deleteEntity(sourceEntity: EntityName) + case insertEntity(destinationEntity: EntityName) + case transformEntity(sourceEntity: EntityName, destinationEntity: EntityName, transformEntity: (_ sourceObject: UnsafeProxyObject, _ destinationObject: UnsafeProxyObject) throws -> Void) + + static func inferredTransformation(sourceObject: UnsafeProxyObject, destinationObject: UnsafeProxyObject) throws -> Void { + + // TODO: + } +// +// case deleteAttribute(sourceEntity: EntityName, sourceAttribute: KeyPath) +// case insertAttribute(destinationEntity: EntityName, destinationAttribute: KeyPath) +// case transformAttribute(sourceEntity: EntityName, sourceAttribute: KeyPath, destinationEntity: EntityName, destinationAttribute: KeyPath, transform: (_ sourceValue: Any?) throws -> Any?) + + var entityMappingSourceEntity: EntityName? { + + switch self { + + case .deleteEntity(let sourceEntity), + .transformEntity(let sourceEntity, _, _): + return sourceEntity + + case .insertEntity: +// .insertAttribute, +// .deleteAttribute, +// .transformAttribute: + return nil + } + } + + var entityMappingDestinationEntity: EntityName? { + + switch self { + + case .insertEntity(let destinationEntity), + .transformEntity(_, let destinationEntity, _): + return destinationEntity + + case .deleteEntity: +// .deleteAttribute, +// .insertAttribute, +// .transformAttribute: + return nil + } + } + +// func attributeMappingSourceAttribute(for sourceEntity: EntityName) -> KeyPath? { +// +// switch self { +// +// case .deleteAttribute(sourceEntity, let sourceAttribute), +// .transformAttribute(sourceEntity, let sourceAttribute, _, _, _): +// return sourceAttribute +// +// case .deleteEntity, +// .insertEntity, +// .insertAttribute, +// .transformEntity: +// return nil +// } +// } +// +// func attributeMappingDestinationAttribute(for destinationEntity: EntityName) -> KeyPath? { +// +// switch self { +// +// case .insertAttribute(destinationEntity, let destinationAttribute), +// .transformAttribute(_, _, destinationEntity, let destinationAttribute, _): +// return destinationAttribute +// +// case .deleteEntity, +// .insertEntity, +// .deleteAttribute, +// .transformEntity: +// return nil +// } +// } + + + + // MARK: Equatable + + public static func == (lhs: CustomMapping, rhs: CustomMapping) -> Bool { + + switch (lhs, rhs) { + + case (.deleteEntity(let sourceEntity1), .deleteEntity(let sourceEntity2)): + return sourceEntity1 == sourceEntity2 + + case (.insertEntity(let destinationEntity1), .insertEntity(let destinationEntity2)): + return destinationEntity1 == destinationEntity2 + + case (.transformEntity(let sourceEntity1, let destinationEntity1, _), .transformEntity(let sourceEntity2, let destinationEntity2, _)): + return sourceEntity1 == sourceEntity2 + && destinationEntity1 == destinationEntity2 + +// case (.deleteAttribute(let sourceEntity1, let sourceAttribute1), .deleteAttribute(let sourceEntity2, let sourceAttribute2)): +// return sourceEntity1 == sourceEntity2 +// && sourceAttribute1 == sourceAttribute2 +// +// case (.insertAttribute(let destinationEntity1, let destinationAttribute1), .insertAttribute(let destinationEntity2, let destinationAttribute2)): +// return destinationEntity1 == destinationEntity2 +// && destinationAttribute1 == destinationAttribute2 +// +// case (.transformAttribute(let sourceEntity1, let sourceAttribute1, let destinationEntity1, let destinationAttribute1, _), .transformAttribute(let sourceEntity2, let sourceAttribute2, let destinationEntity2, let destinationAttribute2, _)): +// return sourceEntity1 == sourceEntity2 +// && sourceAttribute1 == sourceAttribute2 +// && destinationEntity1 == destinationEntity2 +// && destinationAttribute1 == destinationAttribute2 + + default: + return false + } + } + + + // MARK: Hashable + + public var hashValue: Int { + + switch self { + + case .deleteEntity(let sourceEntity): + return sourceEntity.hashValue + + case .insertEntity(let destinationEntity): + return destinationEntity.hashValue + + case .transformEntity(let sourceEntity, let destinationEntity, _): + return sourceEntity.hashValue + ^ destinationEntity.hashValue + +// case .deleteAttribute(let sourceEntity, let sourceAttribute): +// return sourceEntity.hashValue +// ^ sourceAttribute.hashValue +// +// case .insertAttribute(let destinationEntity, let destinationAttribute): +// return destinationEntity.hashValue +// ^ destinationAttribute.hashValue +// +// case .transformAttribute(let sourceEntity, let sourceAttribute, let destinationEntity, let destinationAttribute, _): +// return sourceEntity.hashValue +// ^ sourceAttribute.hashValue +// ^ destinationEntity.hashValue +// ^ destinationAttribute.hashValue + } + } + } + + + // MARK: - UnsafeProxyTransaction + + public final class UnsafeProxyTransaction { + + func fetchAll(entityName: EntityName, _ fetchClauses: FetchClause...) -> [UnsafeProxyObject] { + + return self.fetchAll(entityName: entityName, fetchClauses) + } + + func fetchAll(entityName: EntityName, _ fetchClauses: [FetchClause]) -> [UnsafeProxyObject] { + + // TODO: + fatalError() + } + } + + + // MARK: - UnsafeProxyObject + + public final class UnsafeProxyObject { + + public subscript(kvcKey: KeyPath) -> Any? { + + get { return self.rawObject.cs_accessValueForKVCKey(kvcKey) } + set { self.rawObject.cs_setValue(newValue, forKVCKey: kvcKey) } + } + + + // MARK: Internal + + internal init(_ rawObject: NSManagedObject) { + + self.rawObject = rawObject + } + + + // MARK: Private + + private let rawObject: NSManagedObject + } + + + // MARK: - Public + + public let sourceVersion: ModelVersion + public let destinationVersion: ModelVersion + + public required init(from sourceVersion: ModelVersion, to destinationVersion: ModelVersion, entityMappings: Set = []) { + + CoreStore.assert( + cs_lazy { + + let sources = entityMappings.flatMap({ $0.entityMappingSourceEntity }) + let destinations = entityMappings.flatMap({ $0.entityMappingDestinationEntity }) + return sources.count == Set(sources).count + && destinations.count == Set(destinations).count + }, + "Duplicate source/destination entities found in provided \"entityMappings\" argument." + ) + self.sourceVersion = sourceVersion + self.destinationVersion = destinationVersion + self.entityMappings = entityMappings + } + + + // MARK: Equatable + + public static func == (lhs: CustomSchemaMappingProvider, rhs: CustomSchemaMappingProvider) -> Bool { + + return lhs.sourceVersion == rhs.sourceVersion + && lhs.destinationVersion == rhs.destinationVersion + && type(of: lhs) == type(of: rhs) + } + + + // MARK: Hashable + + public var hashValue: Int { + + return self.sourceVersion.hashValue + ^ self.destinationVersion.hashValue + } + + + // MARK: SchemaMappingProvider + + public func createMappingModel(from sourceSchema: DynamicSchema, to destinationSchema: DynamicSchema, storage: LocalStorage) throws -> (mappingModel: NSMappingModel, migrationType: MigrationType) { + + let sourceModel = sourceSchema.rawModel() + let destinationModel = destinationSchema.rawModel() + + let mappingModel = NSMappingModel() + + let (deleteMappings, insertMappings, transformMappings) = self.resolveEntityMappings( + sourceModel: sourceModel, + destinationModel: destinationModel + ) + func expression(forSource sourceEntity: NSEntityDescription) -> NSExpression { + + return NSExpression(format: "FETCH(FUNCTION($manager, \"fetchRequestForSourceEntityNamed:predicateString:\" , \"\(sourceEntity.name!)\", \"TRUEPREDICATE\"), $manager.sourceContext, NO)") + } + + let sourceEntitiesByName = sourceModel.entitiesByName + let destinationEntitiesByName = destinationModel.entitiesByName + + var entityMappings: [NSEntityMapping] = [] + for case .deleteEntity(let sourceEntityName) in deleteMappings { + + let sourceEntity = sourceEntitiesByName[sourceEntityName]! + + let entityMapping = NSEntityMapping() + entityMapping.sourceEntityName = sourceEntity.name + entityMapping.sourceEntityVersionHash = sourceEntity.versionHash + entityMapping.mappingType = .removeEntityMappingType + entityMapping.sourceExpression = expression(forSource: sourceEntity) + + entityMappings.append(entityMapping) + } + for case .insertEntity(let destinationEntityName) in insertMappings { + + let destinationEntity = destinationEntitiesByName[destinationEntityName]! + + let entityMapping = NSEntityMapping() + entityMapping.destinationEntityName = destinationEntity.name + entityMapping.destinationEntityVersionHash = destinationEntity.versionHash + entityMapping.mappingType = .addEntityMappingType + entityMapping.attributeMappings = autoreleasepool { () -> [NSPropertyMapping] in + + var attributeMappings: [NSPropertyMapping] = [] + for (_, destinationAttribute) in destinationEntity.attributesByName { + + let propertyMapping = NSPropertyMapping() + propertyMapping.name = destinationAttribute.name + propertyMapping.valueExpression = NSExpression(forConstantValue: destinationAttribute.defaultValue) + attributeMappings.append(propertyMapping) + } + return attributeMappings + } + entityMapping.relationshipMappings = autoreleasepool { () -> [NSPropertyMapping] in + + var relationshipMappings: [NSPropertyMapping] = [] + for (_, destinationRelationship) in destinationEntity.relationshipsByName { + + let propertyMapping = NSPropertyMapping() + propertyMapping.name = destinationRelationship.name + relationshipMappings.append(propertyMapping) + } + return relationshipMappings + } + entityMappings.append(entityMapping) + } + for case .transformEntity(let sourceEntityName, let destinationEntityName, let transformEntity) in transformMappings { + + let sourceEntity = sourceEntitiesByName[sourceEntityName]! + let destinationEntity = destinationEntitiesByName[destinationEntityName]! + + let entityMapping = NSEntityMapping() + entityMapping.sourceEntityName = sourceEntity.name + entityMapping.sourceEntityVersionHash = sourceEntity.versionHash + entityMapping.destinationEntityName = destinationEntity.name + entityMapping.destinationEntityVersionHash = destinationEntity.versionHash + entityMapping.mappingType = .transformEntityMappingType + entityMapping.sourceExpression = expression(forSource: sourceEntity) + entityMapping.attributeMappings = autoreleasepool { () -> [NSPropertyMapping] in + + let sourceAttributes = sourceEntity.cs_resolvedAttributeRenamingIdentities() + let destinationAttributes = sourceEntity.cs_resolvedAttributeRenamingIdentities() + + let removedAttributeKeys = Set(sourceAttributes.keys) + .subtracting(destinationAttributes.keys) + let addedAttributeKeys = Set(destinationAttributes.keys) + .subtracting(sourceAttributes.keys) + let copiedAttributeKeys = Set(destinationAttributes.keys) + .subtracting(addedAttributeKeys) + .subtracting(removedAttributeKeys) + .filter({ sourceAttributes[$0]!.versionHash == destinationAttributes[$0]!.versionHash }) + let transformedAttributeKeys = Set(destinationAttributes.keys) + .subtracting(addedAttributeKeys) + .subtracting(removedAttributeKeys) + .subtracting(copiedAttributeKeys) + + var attributeMappings: [NSPropertyMapping] = [] +// for attributeKey in removedAttributeKeys { +// +// let propertyMapping = NSPropertyMapping() +// propertyMapping.name = sourceAttributes[attributeKey]!.attribute.name +// attributeMappings.append(propertyMapping) +// } + for attributeKey in transformedAttributeKeys { + + let sourceAttribute = sourceAttributes[attributeKey]!.attribute + let destinationAttribute = destinationAttributes[attributeKey]!.attribute + // TODO: assert valid and invalid transformations + let propertyMapping = NSPropertyMapping() + propertyMapping.name = destinationAttribute.name + propertyMapping.valueExpression = NSExpression(format: "$source.\(sourceAttribute.name)") + attributeMappings.append(propertyMapping) + } + for attributeKey in copiedAttributeKeys { + + let sourceAttribute = sourceAttributes[attributeKey]!.attribute + let destinationAttribute = destinationAttributes[attributeKey]!.attribute + let propertyMapping = NSPropertyMapping() + propertyMapping.name = destinationAttribute.name + propertyMapping.valueExpression = NSExpression(format: "$source.\(sourceAttribute.name)") + attributeMappings.append(propertyMapping) + } + for attributeKey in addedAttributeKeys { + + let destinationAttribute = destinationAttributes[attributeKey]!.attribute + let propertyMapping = NSPropertyMapping() + propertyMapping.name = destinationAttribute.name + propertyMapping.valueExpression = NSExpression(forConstantValue: destinationAttribute.defaultValue) + attributeMappings.append(propertyMapping) + } + return attributeMappings + } + entityMapping.relationshipMappings = autoreleasepool { () -> [NSPropertyMapping] in + + let sourceRelationships = source.entity.cs_resolvedRelationshipRenamingIdentities() + let destinationRelationships = destination.entity.cs_resolvedRelationshipRenamingIdentities() + + let removedRelationshipKeys = Set(sourceRelationships.keys) + .subtracting(destinationRelationships.keys) + let addedRelationshipKeys = Set(destinationRelationships.keys) + .subtracting(sourceRelationships.keys) + let copiedRelationshipKeys = Set(destinationRelationships.keys) + .subtracting(addedRelationshipKeys) + .subtracting(removedRelationshipKeys) + .filter({ sourceRelationships[$0]!.versionHash == destinationRelationships[$0]!.versionHash }) + let transformedRelationshipKeys = Set(destinationRelationships.keys) + .subtracting(addedRelationshipKeys) + .subtracting(removedRelationshipKeys) + .subtracting(copiedRelationshipKeys) + + var relationshipMappings: [NSPropertyMapping] = [] + for relationshipKey in removedRelationshipKeys { + + let propertyMapping = NSPropertyMapping() + propertyMapping.name = sourceRelationships[relationshipKey]!.relationship.name + relationshipMappings.append(propertyMapping) + } + for attributeKey in transformedRelationshipKeys { + + let sourceRelationship = sourceRelationships[attributeKey]!.relationship + let destinationRelationship = destinationRelationships[attributeKey]!.relationship + // TODO: assert valid and invalid transformations + let propertyMapping = NSPropertyMapping() + propertyMapping.name = destinationRelationship.name + propertyMapping.valueExpression = NSExpression(format: "$source.\(sourceRelationship.name)") + relationshipMappings.append(propertyMapping) + } + for attributeKey in copiedRelationshipKeys { + + let sourceRelationship = sourceRelationships[attributeKey]!.relationship + let destinationRelationship = destinationRelationships[attributeKey]!.relationship + let propertyMapping = NSPropertyMapping() + propertyMapping.name = destinationRelationship.name + propertyMapping.valueExpression = NSExpression(format: "$source.\(sourceRelationship.name)") + relationshipMappings.append(propertyMapping) + } + for attributeKey in addedRelationshipKeys { + + let destinationRelationship = destinationRelationships[attributeKey]!.relationship + let propertyMapping = NSPropertyMapping() + propertyMapping.name = destinationRelationship.name + relationshipMappings.append(propertyMapping) + } + return relationshipMappings + } + entityMappings.append(entityMapping) + } + for entityKey in copiedEntityKeys { + + let source = sourceEntities[entityKey]! + let destination = destinationEntities[entityKey]! + + let entityMapping = NSEntityMapping() + entityMapping.sourceEntityName = source.entity.name + entityMapping.sourceEntityVersionHash = source.versionHash + entityMapping.destinationEntityName = destination.entity.name + entityMapping.destinationEntityVersionHash = destination.versionHash + entityMapping.mappingType = .copyEntityMappingType + entityMapping.sourceExpression = expression(forSource: source.entity) + entityMapping.attributeMappings = autoreleasepool { () -> [NSPropertyMapping] in + + var attributeMappings: [NSPropertyMapping] = [] + for (_, sourceAttribute) in source.entity.attributesByName { + + let propertyMapping = NSPropertyMapping() + propertyMapping.name = sourceAttribute.name + propertyMapping.valueExpression = NSExpression(format: "$source.\(sourceAttribute.name)") + attributeMappings.append(propertyMapping) + } + return attributeMappings + } + entityMapping.relationshipMappings = autoreleasepool { () -> [NSPropertyMapping] in + + var relationshipMappings: [NSPropertyMapping] = [] + for (_, sourceRelationship) in source.entity.relationshipsByName { + + let propertyMapping = NSPropertyMapping() + propertyMapping.name = sourceRelationship.name + propertyMapping.valueExpression = NSExpression(format: "$source.\(sourceRelationship.name)") + relationshipMappings.append(propertyMapping) + } + return relationshipMappings + } + entityMappings.append(entityMapping) + } + + mappingModel.entityMappings = entityMappings + return ( + mappingModel, + .heavyweight( + sourceVersion: self.sourceVersion, + destinationVersion: self.destinationVersion + ) + ) + } + + + // MARK: Private + + // MARK: - CustomEntityMigrationPolicy + + private final class CustomEntityMigrationPolicy: NSEntityMigrationPolicy { + + override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws { + + try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager) + } + + override func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws { + + try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager) + } + } + + + // MARK: - + + private let entityMappings: Set + + private func resolveEntityMappings(sourceModel: NSManagedObjectModel, destinationModel: NSManagedObjectModel) -> (delete: Set, insert: Set, transform: Set) { + + var deleteMappings: Set = [] + var insertMappings: Set = [] + var transformMappings: Set = [] + var allMappedSourceKeys: [KeyPath: KeyPath] = [:] + var allMappedDestinationKeys: [KeyPath: KeyPath] = [:] + + let sourceRenamingIdentifiers = sourceModel.cs_resolvedRenamingIdentities() + let sourceEntityNames = sourceModel.entitiesByName + let destinationRenamingIdentifiers = destinationModel.cs_resolvedRenamingIdentities() + let destinationEntityNames = destinationModel.entitiesByName + + let removedRenamingIdentifiers = Set(sourceRenamingIdentifiers.keys) + .subtracting(destinationRenamingIdentifiers.keys) + let addedRenamingIdentifiers = Set(destinationRenamingIdentifiers.keys) + .subtracting(sourceRenamingIdentifiers.keys) + let transformedRenamingIdentifiers = Set(destinationRenamingIdentifiers.keys) + .subtracting(addedRenamingIdentifiers) + .subtracting(removedRenamingIdentifiers) + + // First pass: resolve source-destination entities + for mapping in self.entityMappings { + + switch mapping { + + case .deleteEntity(let sourceEntity): + CoreStore.assert( + sourceEntityNames[sourceEntity] != nil, + "A \(cs_typeName(CustomMapping.self)) with value '\(mapping)' passed to \(cs_typeName(CustomSchemaMappingProvider.self)) could not be mapped to any \(cs_typeName(NSEntityDescription.self)) from the source \(cs_typeName(NSManagedObjectModel.self))." + ) + CoreStore.assert( + allMappedSourceKeys[sourceEntity] == nil, + "Duplicate \(cs_typeName(CustomMapping.self))s found for source entity name \"\(sourceEntity)\" in \(cs_typeName(CustomSchemaMappingProvider.self))." + ) + deleteMappings.insert(mapping) + allMappedSourceKeys[sourceEntity] = "" + + case .insertEntity(let destinationEntity): + CoreStore.assert( + destinationEntityNames[destinationEntity] != nil, + "A \(cs_typeName(CustomMapping.self)) with value '\(mapping)' passed to \(cs_typeName(CustomSchemaMappingProvider.self)) could not be mapped to any \(cs_typeName(NSEntityDescription.self)) from the destination \(cs_typeName(NSManagedObjectModel.self))." + ) + CoreStore.assert( + allMappedDestinationKeys[destinationEntity] == nil, + "Duplicate \(cs_typeName(CustomMapping.self))s found for destination entity name \"\(destinationEntity)\" in \(cs_typeName(CustomSchemaMappingProvider.self))." + ) + insertMappings.insert(mapping) + allMappedDestinationKeys[destinationEntity] = "" + + case .transformEntity(let sourceEntity, let destinationEntity, _): + CoreStore.assert( + sourceEntityNames[sourceEntity] != nil, + "A \(cs_typeName(CustomMapping.self)) with value '\(mapping)' passed to \(cs_typeName(CustomSchemaMappingProvider.self)) could not be mapped to any \(cs_typeName(NSEntityDescription.self)) from the source \(cs_typeName(NSManagedObjectModel.self))." + ) + CoreStore.assert( + destinationEntityNames[destinationEntity] != nil, + "A \(cs_typeName(CustomMapping.self)) with value '\(mapping)' passed to \(cs_typeName(CustomSchemaMappingProvider.self)) could not be mapped to any \(cs_typeName(NSEntityDescription.self)) from the destination \(cs_typeName(NSManagedObjectModel.self))." + ) + CoreStore.assert( + allMappedSourceKeys[sourceEntity] == nil, + "Duplicate \(cs_typeName(CustomMapping.self))s found for source entity name \"\(sourceEntity)\" in \(cs_typeName(CustomSchemaMappingProvider.self))." + ) + CoreStore.assert( + allMappedDestinationKeys[destinationEntity] == nil, + "Duplicate \(cs_typeName(CustomMapping.self))s found for destination entity name \"\(destinationEntity)\" in \(cs_typeName(CustomSchemaMappingProvider.self))." + ) + transformMappings.insert(mapping) + allMappedSourceKeys[sourceEntity] = destinationEntity + allMappedDestinationKeys[destinationEntity] = sourceEntity + } + + for renamingIdentifier in transformedRenamingIdentifiers { + + let sourceEntity = sourceRenamingIdentifiers[renamingIdentifier]!.entity + let destinationEntity = destinationRenamingIdentifiers[renamingIdentifier]!.entity + let sourceEntityName = sourceEntity.name! + let destinationEntityName = destinationEntity.name! + switch (allMappedSourceKeys[sourceEntityName], allMappedDestinationKeys[destinationEntityName]) { + + case (nil, nil): + transformMappings.insert( + .transformEntity( + sourceEntity: sourceEntityName, + destinationEntity: destinationEntityName, + transformEntity: CustomMapping.inferredTransformation + ) + ) + allMappedSourceKeys[sourceEntityName] = destinationEntityName + allMappedDestinationKeys[destinationEntityName] = sourceEntityName + + case (""?, nil): + insertMappings.insert(.insertEntity(destinationEntity: destinationEntityName)) + allMappedDestinationKeys[destinationEntityName] = "" + + case (nil, ""?): + deleteMappings.insert(.deleteEntity(sourceEntity: sourceEntityName)) + allMappedSourceKeys[sourceEntityName] = "" + + default: + continue + } + } + for renamingIdentifier in removedRenamingIdentifiers { + + let sourceEntity = sourceRenamingIdentifiers[renamingIdentifier]!.entity + let sourceEntityName = sourceEntity.name! + switch allMappedSourceKeys[sourceEntityName] { + + case nil: + deleteMappings.insert(.deleteEntity(sourceEntity: sourceEntityName)) + allMappedSourceKeys[sourceEntityName] = "" + + default: + continue + } + } + for renamingIdentifier in addedRenamingIdentifiers { + + let destinationEntity = destinationRenamingIdentifiers[renamingIdentifier]!.entity + let destinationEntityName = destinationEntity.name! + switch allMappedDestinationKeys[destinationEntityName] { + + case nil: + insertMappings.insert(.insertEntity(destinationEntity: destinationEntityName)) + allMappedDestinationKeys[destinationEntityName] = "" + + default: + continue + } + } + } + return (deleteMappings, insertMappings, transformMappings) + } +} diff --git a/Sources/Migrating/Schema Mapping Providers/InferredSchemaMappingProvider.swift b/Sources/Migrating/Schema Mapping Providers/InferredSchemaMappingProvider.swift new file mode 100644 index 0000000..9bab31f --- /dev/null +++ b/Sources/Migrating/Schema Mapping Providers/InferredSchemaMappingProvider.swift @@ -0,0 +1,89 @@ +// +// InferredSchemaMappingProvider.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - InferredSchemaMappingProvider + +final class InferredSchemaMappingProvider: Hashable, SchemaMappingProvider { + + // MARK: Equatable + + public static func == (lhs: InferredSchemaMappingProvider, rhs: InferredSchemaMappingProvider) -> Bool { + + return true + } + + + // MARK: Hashable + + public var hashValue: Int { + + return ObjectIdentifier(type(of: self)).hashValue + } + + + // MARK: SchemaMappingProvider + + public func createMappingModel(from sourceSchema: DynamicSchema, to destinationSchema: DynamicSchema, storage: LocalStorage) throws -> (mappingModel: NSMappingModel, migrationType: MigrationType) { + + let sourceModel = sourceSchema.rawModel() + let destinationModel = destinationSchema.rawModel() + + if let mappingModel = NSMappingModel( + from: Bundle.allBundles, + forSourceModel: sourceModel, + destinationModel: destinationModel) { + + return ( + mappingModel, + .heavyweight( + sourceVersion: sourceSchema.modelVersion, + destinationVersion: destinationSchema.modelVersion + ) + ) + } + do { + + let mappingModel = try NSMappingModel.inferredMappingModel( + forSourceModel: sourceModel, + destinationModel: destinationModel + ) + return ( + mappingModel, + .lightweight( + sourceVersion: sourceSchema.modelVersion, + destinationVersion: destinationSchema.modelVersion + ) + ) + } + catch { + + throw CoreStoreError(error) + } + } +} diff --git a/Sources/Migrating/Schema Mapping Providers/SchemaMappingProvider.swift b/Sources/Migrating/Schema Mapping Providers/SchemaMappingProvider.swift new file mode 100644 index 0000000..b538614 --- /dev/null +++ b/Sources/Migrating/Schema Mapping Providers/SchemaMappingProvider.swift @@ -0,0 +1,35 @@ +// +// SchemaMappingProvider.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - SchemaMappingProvider + +public protocol SchemaMappingProvider { + + func createMappingModel(from sourceSchema: DynamicSchema, to destinationSchema: DynamicSchema, storage: LocalStorage) throws -> (mappingModel: NSMappingModel, migrationType: MigrationType) +} diff --git a/Sources/Migrating/Schema Mapping Providers/XcodeSchemaMappingProvider.swift b/Sources/Migrating/Schema Mapping Providers/XcodeSchemaMappingProvider.swift new file mode 100644 index 0000000..da80da3 --- /dev/null +++ b/Sources/Migrating/Schema Mapping Providers/XcodeSchemaMappingProvider.swift @@ -0,0 +1,91 @@ +// +// XcodeSchemaMappingProvider.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - XcodeSchemaMappingProvider + +final class XcodeSchemaMappingProvider: Hashable, SchemaMappingProvider { + + public let sourceVersion: ModelVersion + public let destinationVersion: ModelVersion + public let mappingModelBundle: Bundle + + public required init(from sourceVersion: ModelVersion, to destinationVersion: ModelVersion, mappingModelBundle: Bundle) { + + self.sourceVersion = sourceVersion + self.destinationVersion = destinationVersion + self.mappingModelBundle = mappingModelBundle + } + + + // MARK: Equatable + + public static func == (lhs: XcodeSchemaMappingProvider, rhs: XcodeSchemaMappingProvider) -> Bool { + + return lhs.sourceVersion == rhs.sourceVersion + && lhs.destinationVersion == rhs.destinationVersion + && lhs.mappingModelBundle == rhs.mappingModelBundle + } + + + // MARK: Hashable + + public var hashValue: Int { + + return self.sourceVersion.hashValue + ^ self.destinationVersion.hashValue + } + + + // MARK: SchemaMappingProvider + + public func createMappingModel(from sourceSchema: DynamicSchema, to destinationSchema: DynamicSchema, storage: LocalStorage) throws -> (mappingModel: NSMappingModel, migrationType: MigrationType) { + + let sourceModel = sourceSchema.rawModel() + let destinationModel = destinationSchema.rawModel() + + if let mappingModel = NSMappingModel( + from: [self.mappingModelBundle], + forSourceModel: sourceModel, + destinationModel: destinationModel) { + + return ( + mappingModel, + .heavyweight( + sourceVersion: sourceSchema.modelVersion, + destinationVersion: destinationSchema.modelVersion + ) + ) + } + throw CoreStoreError.mappingModelNotFound( + localStoreURL: storage.fileURL, + targetModel: destinationModel, + targetModelVersion: destinationSchema.modelVersion + ) + } +} diff --git a/Sources/ObjectiveC/CSDataStack.swift b/Sources/ObjectiveC/CSDataStack.swift index aca930b..adbdcb4 100644 --- a/Sources/ObjectiveC/CSDataStack.swift +++ b/Sources/ObjectiveC/CSDataStack.swift @@ -125,7 +125,7 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType { return bridge(error) { - try self.bridgeToSwift.addStorageAndWait(InMemoryStore.self) + try self.bridgeToSwift.addStorageAndWait(InMemoryStore()) } } @@ -143,7 +143,7 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType { return bridge(error) { - try self.bridgeToSwift.addStorageAndWait(SQLiteStore.self) + try self.bridgeToSwift.addStorageAndWait(SQLiteStore()) } } diff --git a/Sources/ObjectiveC/CSDynamicSchema.swift b/Sources/ObjectiveC/CSDynamicSchema.swift new file mode 100644 index 0000000..39004d0 --- /dev/null +++ b/Sources/ObjectiveC/CSDynamicSchema.swift @@ -0,0 +1,51 @@ +// +// CSDynamicSchema.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - CSDynamicSchema + +/** + The `CSDynamicSchema` serves as the Objective-C bridging type for `DynamicSchema`. + + - SeeAlso: `DynamicSchema` + */ +@objc +public protocol CSDynamicSchema { + + /** + The version string for this model schema. + */ + @objc + var modelVersion: ModelVersion { get } + + /** + Do not call this directly. The `NSManagedObjectModel` for this schema may be created lazily and using this method directly may affect the integrity of the model. + */ + @objc + func rawModel() -> NSManagedObjectModel +} diff --git a/Sources/ObjectiveC/CSLegacyXcodeDataModelSchema.swift b/Sources/ObjectiveC/CSLegacyXcodeDataModelSchema.swift new file mode 100644 index 0000000..fafab43 --- /dev/null +++ b/Sources/ObjectiveC/CSLegacyXcodeDataModelSchema.swift @@ -0,0 +1,109 @@ +// +// CSLegacyXcodeDataModelSchema.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - CSLegacyXcodeDataModelSchema + +/** + The `CSLegacyXcodeDataModelSchema` serves as the Objective-C bridging type for `LegacyXcodeDataModelSchema`. + + - SeeAlso: `LegacyXcodeDataModelSchema` + */ +@objc +public final class CSLegacyXcodeDataModelSchema: NSObject, CSDynamicSchema, CoreStoreObjectiveCType { + + @objc + public required init(modelName: ModelVersion, model: NSManagedObjectModel) { + + self.bridgeToSwift = LegacyXcodeDataModelSchema( + modelName: modelName, + model: model + ) + } + + + // MARK: NSObject + + public override var hash: Int { + + return ObjectIdentifier(self.bridgeToSwift).hashValue + } + + public override func isEqual(_ object: Any?) -> Bool { + + guard let object = object as? CSLegacyXcodeDataModelSchema else { + + return false + } + return self.bridgeToSwift === object.bridgeToSwift + } + + public override var description: String { + + return "(\(String(reflecting: type(of: self)))) \(self.bridgeToSwift.coreStoreDumpString)" + } + + + // MARK: CSDynamicSchema + + @objc + public var modelVersion: ModelVersion { + + return self.bridgeToSwift.modelVersion + } + + @objc + public func rawModel() -> NSManagedObjectModel { + + return self.bridgeToSwift.rawModel() + } + + + // MARK: CoreStoreObjectiveCType + + public let bridgeToSwift: LegacyXcodeDataModelSchema + + public required init(_ swiftValue: LegacyXcodeDataModelSchema) { + + self.bridgeToSwift = swiftValue + super.init() + } +} + + +// MARK: - LegacyXcodeDataModelSchema + +extension LegacyXcodeDataModelSchema: CoreStoreSwiftType { + + // MARK: CoreStoreSwiftType + + public var bridgeToObjectiveC: CSLegacyXcodeDataModelSchema { + + return CSLegacyXcodeDataModelSchema(self) + } +} diff --git a/Sources/ObjectiveC/CSSQliteStore.swift b/Sources/ObjectiveC/CSSQliteStore.swift index 5bb5164..6860dfc 100644 --- a/Sources/ObjectiveC/CSSQliteStore.swift +++ b/Sources/ObjectiveC/CSSQliteStore.swift @@ -40,19 +40,18 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT /** Initializes an SQLite store interface from the given SQLite file URL. When this instance is passed to the `CSDataStack`'s `-addStorage*:` methods, a new SQLite file will be created if it does not exist. + - Important: Initializing `CSSQLiteStore`s with custom migration mapping models is currently not supported. Create an `SQLiteStore` instance from Swift code and bridge the instance to Objective-C using its `SQLiteStore.bridgeToObjectiveC` property. - 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 mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration. - parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `CSLocalStorageOptionsNone`. */ @objc - public convenience init(fileURL: URL, configuration: ModelConfiguration, mappingModelBundles: [Bundle]?, localStorageOptions: Int) { + public convenience init(fileURL: URL, configuration: ModelConfiguration, localStorageOptions: Int) { self.init( SQLiteStore( fileURL: fileURL, configuration: configuration, - mappingModelBundles: mappingModelBundles ?? Bundle.allBundles, localStorageOptions: LocalStorageOptions(rawValue: localStorageOptions) ) ) @@ -61,29 +60,27 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT /** Initializes an SQLite store interface from the given SQLite file name. When this instance is passed to the `CSDataStack`'s `-addStorage*:` methods, a new SQLite file will be created if it does not exist. - - Warning: The default SQLite file location for the `CSLegacySQLiteStore` and `CSSQLiteStore` are different. If the app was depending on CoreStore's default directories prior to 2.0.0, make sure to use `CSLegacySQLiteStore` instead of `CSSQLiteStore`. + - Important: Initializing `CSSQLiteStore`s with custom migration mapping models is currently not supported. Create an `SQLiteStore` instance from Swift code and bridge the instance to Objective-C using its `SQLiteStore.bridgeToObjectiveC` property. - 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 mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration - parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `[CSLocalStorageOptions none]`. */ @objc - public convenience init(fileName: String, configuration: ModelConfiguration, mappingModelBundles: [Bundle]?, localStorageOptions: Int) { + public convenience init(fileName: String, configuration: ModelConfiguration, localStorageOptions: Int) { self.init( SQLiteStore( fileName: fileName, configuration: configuration, - mappingModelBundles: mappingModelBundles ?? Bundle.allBundles, localStorageOptions: LocalStorageOptions(rawValue: localStorageOptions) ) ) } /** - Initializes an `CSSQLiteStore` with an all-default settings: a `fileURL` pointing to a ".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`. + Initializes an `CSSQLiteStore` with an all-default settings: a `fileURL` pointing to a ".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 `[CSLocalStorageOptions none]`. - - Warning: The default SQLite file location for the `CSLegacySQLiteStore` and `CSSQLiteStore` are different. If the app was depending on CoreStore's default directories prior to 2.0.0, make sure to use `CSLegacySQLiteStore` instead of `CSSQLiteStore`. + - Important: Initializing `CSSQLiteStore`s with custom migration mapping models is currently not supported. Create an `SQLiteStore` instance from Swift code and bridge the instance to Objective-C using its `SQLiteStore.bridgeToObjectiveC` property. */ @objc public convenience override init() { @@ -104,12 +101,12 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT } /** - The `NSBundle`s from which to search mapping models for migrations + An array of `SchemaMappingProvider`s that provides the complete mapping models for custom migrations. This is currently only supported for Swift code. */ @objc - public var mappingModelBundles: [Bundle] { - - return self.bridgeToSwift.mappingModelBundles + public var migrationMappingProviders: [Any] { + + return self.bridgeToSwift.migrationMappingProviders } /** @@ -194,6 +191,55 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT self.bridgeToSwift = swiftValue super.init() } + + + // MARK: Deprecated + + /** + Initializes an SQLite store interface from the given SQLite file URL. When this instance is passed to the `CSDataStack`'s `-addStorage*:` methods, a new SQLite file will be created if it does not exist. + + - Important: Initializing `CSSQLiteStore`s with custom migration mapping models is currently not supported. Create an `SQLiteStore` instance from Swift code and bridge the instance to Objective-C using its `SQLiteStore.bridgeToObjectiveC` property. + - 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 mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration. + - parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `CSLocalStorageOptionsNone`. + */ + @available(*, deprecated: 3.1, message: "The `mappingModelBundles` argument of this method is ignored. Use the new -[CSSQLiteStore initWithFileURL:configuration:localStorageOptions:]) initializer instead.") + @objc + public convenience init(fileURL: URL, configuration: ModelConfiguration, mappingModelBundles: [Bundle]?, localStorageOptions: Int) { + + self.init( + SQLiteStore( + fileURL: fileURL, + configuration: configuration, + mappingModelBundles: mappingModelBundles ?? Bundle.allBundles, + localStorageOptions: LocalStorageOptions(rawValue: localStorageOptions) + ) + ) + } + + /** + Initializes an SQLite store interface from the given SQLite file name. When this instance is passed to the `CSDataStack`'s `-addStorage*:` methods, a new SQLite file will be created if it does not exist. + + - Important: Initializing `CSSQLiteStore`s with custom migration mapping models is currently not supported. Create an `SQLiteStore` instance from Swift code and bridge the instance to Objective-C using its `SQLiteStore.bridgeToObjectiveC` property. + - 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 mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration + - parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `[CSLocalStorageOptions none]`. + */ + @available(*, deprecated: 3.1, message: "The `mappingModelBundles` argument of this method is ignored. Use the new -[CSSQLiteStore initWithFileName:configuration:localStorageOptions:]) initializer instead.") + @objc + public convenience init(fileName: String, configuration: ModelConfiguration, mappingModelBundles: [Bundle]?, localStorageOptions: Int) { + + self.init( + SQLiteStore( + fileName: fileName, + configuration: configuration, + mappingModelBundles: mappingModelBundles ?? Bundle.allBundles, + localStorageOptions: LocalStorageOptions(rawValue: localStorageOptions) + ) + ) + } } diff --git a/Sources/ObjectiveC/CSStorageInterface.swift b/Sources/ObjectiveC/CSStorageInterface.swift index a18cb74..33b0a55 100644 --- a/Sources/ObjectiveC/CSStorageInterface.swift +++ b/Sources/ObjectiveC/CSStorageInterface.swift @@ -106,10 +106,10 @@ public protocol CSLocalStorage: CSStorageInterface { var fileURL: URL { get } /** - The `NSBundle`s from which to search mapping models for migrations + An array of `SchemaMappingProvider`s that provides the complete mapping models for custom migrations. This is currently only supported for Swift code. */ @objc - var mappingModelBundles: [Bundle] { get } + var migrationMappingProviders: [Any] { get } /** Options that tell the `CSDataStack` how to setup the persistent store diff --git a/Sources/ObjectiveC/CSXcodeDataModelSchema.swift b/Sources/ObjectiveC/CSXcodeDataModelSchema.swift new file mode 100644 index 0000000..336480d --- /dev/null +++ b/Sources/ObjectiveC/CSXcodeDataModelSchema.swift @@ -0,0 +1,109 @@ +// +// CSXcodeDataModelSchema.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import CoreData +import Foundation + + +// MARK: - CSXcodeDataModelSchema + +/** + The `CSXcodeDataModelSchema` serves as the Objective-C bridging type for `XcodeDataModelSchema`. + + - SeeAlso: `XcodeDataModelSchema` + */ +@objc +public final class CSXcodeDataModelSchema: NSObject, CSDynamicSchema, CoreStoreObjectiveCType { + + @objc + public required init(modelVersion: ModelVersion, modelVersionFileURL: URL) { + + self.bridgeToSwift = XcodeDataModelSchema( + modelVersion: modelVersion, + modelVersionFileURL: modelVersionFileURL + ) + } + + + // MARK: NSObject + + public override var hash: Int { + + return ObjectIdentifier(self.bridgeToSwift).hashValue + } + + public override func isEqual(_ object: Any?) -> Bool { + + guard let object = object as? CSXcodeDataModelSchema else { + + return false + } + return self.bridgeToSwift === object.bridgeToSwift + } + + public override var description: String { + + return "(\(String(reflecting: type(of: self)))) \(self.bridgeToSwift.coreStoreDumpString)" + } + + + // MARK: CSDynamicSchema + + @objc + public var modelVersion: ModelVersion { + + return self.bridgeToSwift.modelVersion + } + + @objc + public func rawModel() -> NSManagedObjectModel { + + return self.bridgeToSwift.rawModel() + } + + + // MARK: CoreStoreObjectiveCType + + public let bridgeToSwift: XcodeDataModelSchema + + public required init(_ swiftValue: XcodeDataModelSchema) { + + self.bridgeToSwift = swiftValue + super.init() + } +} + + +// MARK: - XcodeDataModelSchema + +extension XcodeDataModelSchema: CoreStoreSwiftType { + + // MARK: CoreStoreSwiftType + + public var bridgeToObjectiveC: CSXcodeDataModelSchema { + + return CSXcodeDataModelSchema(self) + } +} diff --git a/Sources/ObjectiveC/CoreStoreBridge.swift b/Sources/ObjectiveC/CoreStoreBridge.swift index 468f3c0..7d77680 100644 --- a/Sources/ObjectiveC/CoreStoreBridge.swift +++ b/Sources/ObjectiveC/CoreStoreBridge.swift @@ -76,6 +76,11 @@ internal func bridge(_ closure: () -> T) -> T.ObjectiveCT return closure().bridgeToObjectiveC } +internal func bridge(_ closure: () -> [T]) -> [T.ObjectiveCType] { + + return closure().map { $0.bridgeToObjectiveC } +} + internal func bridge(_ closure: () -> T?) -> T.ObjectiveCType? { return closure()?.bridgeToObjectiveC diff --git a/Sources/Setup/CoreStore+Setup.swift b/Sources/Setup/CoreStore+Setup.swift index 8e2cef2..37cc29a 100644 --- a/Sources/Setup/CoreStore+Setup.swift +++ b/Sources/Setup/CoreStore+Setup.swift @@ -81,22 +81,7 @@ public extension CoreStore { @discardableResult public static func addStorageAndWait() throws -> SQLiteStore { - return try self.defaultStack.addStorageAndWait(SQLiteStore.self) - } - - /** - Creates a `StorageInterface` of the specified store type with default values and adds it to the `defaultStack`. This method blocks until completion. - ``` - try CoreStore.addStorageAndWait(InMemoryStore.self) - ``` - - parameter storeType: the `StorageInterface` type - - throws: a `CoreStoreError` value indicating the failure - - returns: the `StorageInterface` added to the `defaultStack` - */ - @discardableResult - public static func addStorageAndWait(_ storeType: T.Type) throws -> T where T: DefaultInitializableStore { - - return try self.defaultStack.addStorageAndWait(storeType.init()) + return try self.defaultStack.addStorageAndWait(SQLiteStore()) } /** @@ -114,21 +99,6 @@ public extension CoreStore { return try self.defaultStack.addStorageAndWait(storage) } - /** - Creates a `LocalStorageInterface` of the specified store type with default values and adds it to the `defaultStack`. This method blocks until completion. - ``` - try CoreStore.addStorageAndWait(SQLiteStore.self) - ``` - - parameter storeType: the `LocalStorageInterface` type - - throws: a `CoreStoreError` value indicating the failure - - returns: the local storage added to the `defaultStack` - */ - @discardableResult - public static func addStorageAndWait(_ storageType: T.Type) throws -> T where T: DefaultInitializableStore { - - return try self.defaultStack.addStorageAndWait(storageType.init()) - } - /** Adds a `LocalStorage` to the `defaultStack` and blocks until completion. ``` diff --git a/Sources/Setup/DataStack.swift b/Sources/Setup/DataStack.swift index 7c926ed..c8a42ff 100644 --- a/Sources/Setup/DataStack.swift +++ b/Sources/Setup/DataStack.swift @@ -142,7 +142,7 @@ public final class DataStack: Equatable { continue case .coreStore: - guard let anyEntity = entityDescription.anyEntity else { + guard let anyEntity = entityDescription.coreStoreEntity else { continue } @@ -191,22 +191,7 @@ public final class DataStack: Equatable { @discardableResult public func addStorageAndWait() throws -> SQLiteStore { - return try self.addStorageAndWait(SQLiteStore.self) - } - - /** - Creates a `StorageInterface` of the specified store type with default values and adds it to the stack. This method blocks until completion. - ``` - try dataStack.addStorageAndWait(InMemoryStore.self) - ``` - - parameter storeType: the `StorageInterface` type - - throws: a `CoreStoreError` value indicating the failure - - returns: the `StorageInterface` added to the stack - */ - @discardableResult - public func addStorageAndWait(_ storeType: T.Type) throws -> T where T: DefaultInitializableStore { - - return try self.addStorageAndWait(storeType.init()) + return try self.addStorageAndWait(SQLiteStore()) } /** @@ -248,21 +233,6 @@ public final class DataStack: Equatable { } } - /** - Creates a `LocalStorageInterface` of the specified store type with default values and adds it to the stack. This method blocks until completion. - ``` - try dataStack.addStorageAndWait(SQLiteStore.self) - ``` - - parameter storeType: the `LocalStorageInterface` type - - throws: a `CoreStoreError` value indicating the failure - - returns: the local storage added to the stack - */ - @discardableResult - public func addStorageAndWait(_ storageType: T.Type) throws -> T where T: DefaultInitializableStore { - - return try self.addStorageAndWait(storageType.init()) - } - /** Adds a `LocalStorage` to the stack and blocks until completion. ``` diff --git a/Sources/Setup/Dynamic Models/Dynamic Schema/CoreStoreSchema.swift b/Sources/Setup/Dynamic Models/Dynamic Schema/CoreStoreSchema.swift index 869c52e..8a6df67 100644 --- a/Sources/Setup/Dynamic Models/Dynamic Schema/CoreStoreSchema.swift +++ b/Sources/Setup/Dynamic Models/Dynamic Schema/CoreStoreSchema.swift @@ -203,7 +203,7 @@ public final class CoreStoreSchema: DynamicSchema { private static func firstPassCreateEntityDescription(from entity: AnyEntity) -> NSEntityDescription { let entityDescription = NSEntityDescription() - entityDescription.anyEntity = entity + entityDescription.coreStoreEntity = entity entityDescription.name = entity.entityName entityDescription.isAbstract = entity.isAbstract entityDescription.managedObjectClassName = NSStringFromClass(NSManagedObject.self) diff --git a/Sources/Setup/Dynamic Models/SchemaHistory.swift b/Sources/Setup/Dynamic Models/SchemaHistory.swift index 2921a5f..155abcc 100644 --- a/Sources/Setup/Dynamic Models/SchemaHistory.swift +++ b/Sources/Setup/Dynamic Models/SchemaHistory.swift @@ -223,6 +223,11 @@ public final class SchemaHistory: ExpressibleByArrayLiteral { return self.schemaByVersion[modelVersion]?.rawModel() } + internal func schema(for modelVersion: ModelVersion) -> DynamicSchema? { + + return self.schemaByVersion[modelVersion] + } + internal func schema(for storeMetadata: [String: Any]) -> DynamicSchema? { guard let modelHashes = storeMetadata[NSStoreModelVersionHashesKey] as! [String: Data]? else { diff --git a/Sources/Setup/StorageInterfaces/InMemoryStore.swift b/Sources/Setup/StorageInterfaces/InMemoryStore.swift index 67f6874..107799a 100644 --- a/Sources/Setup/StorageInterfaces/InMemoryStore.swift +++ b/Sources/Setup/StorageInterfaces/InMemoryStore.swift @@ -31,7 +31,7 @@ import CoreData /** A storage interface that is backed only by memory. */ -public final class InMemoryStore: StorageInterface, DefaultInitializableStore { +public final class InMemoryStore: StorageInterface { /** Initializes an `InMemoryStore` for the specified configuration @@ -42,9 +42,6 @@ public final class InMemoryStore: StorageInterface, DefaultInitializableStore { self.configuration = configuration } - - // MARK: DefaultInitializableStore - /** Initializes an `InMemoryStore` with the "Default" configuration */ diff --git a/Sources/Setup/StorageInterfaces/LegacySQLiteStore.swift b/Sources/Setup/StorageInterfaces/LegacySQLiteStore.swift index db8f1e9..5465a99 100644 --- a/Sources/Setup/StorageInterfaces/LegacySQLiteStore.swift +++ b/Sources/Setup/StorageInterfaces/LegacySQLiteStore.swift @@ -34,7 +34,8 @@ import CoreData - 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 final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore { +@available(*, obsoleted: 3.1, message: "`LegacySQLiteStore` previous users should now use `SQLiteStore`'s new SQLiteStore.legacy(fileName:configuration:migrationMappingProviders:localStorageOptions:) or SQLiteStore.legacy() methods to create an `SQLiteStore` with legacy paths.") +public final class LegacySQLiteStore: LocalStorage { /** Initializes an SQLite store interface from the given SQLite file URL. When this instance is passed to the `DataStack`'s `addStorage()` methods, a new SQLite file will be created if it does not exist. @@ -44,12 +45,10 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore { - parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration. - 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`. */ + @available(*, obsoleted: 3.1, message: "Use `SQLiteStore`'s new SQLiteStore.init(fileURL:configuration:migrationMappingProviders:localStorageOptions:) initializer") public init(fileURL: URL, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) { - self.fileURL = fileURL - self.configuration = configuration - self.mappingModelBundles = mappingModelBundles - self.localStorageOptions = localStorageOptions + fatalError() } /** @@ -61,31 +60,21 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore { - parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration. - 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`. */ + @available(*, obsoleted: 3.1, message: "Use `SQLiteStore`'s new SQLiteStore.legacy(fileName:configuration:migrationMappingProviders:localStorageOptions:) method.") public init(fileName: String, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) { - self.fileURL = LegacySQLiteStore.defaultRootDirectory.appendingPathComponent( - fileName, - isDirectory: false - ) - self.configuration = configuration - self.mappingModelBundles = mappingModelBundles - self.localStorageOptions = localStorageOptions + fatalError() } - - // MARK: DefaultInitializableStore - /** Initializes an `LegacySQLiteStore` with an all-default settings: a `fileURL` pointing to a ".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`. */ + @available(*, obsoleted: 3.1, message: "Use `SQLiteStore`'s new SQLiteStore.legacy() method.") public init() { - self.fileURL = LegacySQLiteStore.defaultFileURL - self.configuration = nil - self.mappingModelBundles = Bundle.allBundles - self.localStorageOptions = nil + fatalError() } @@ -153,9 +142,9 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore { public let fileURL: URL /** - The `NSBundle`s from which to search mapping models for migrations + An array of `SchemaMappingProviders` that provides the complete mapping models for custom migrations. */ - public let mappingModelBundles: [Bundle] + public let migrationMappingProviders: [SchemaMappingProvider] /** Options that tell the `DataStack` how to setup the persistent store @@ -231,26 +220,6 @@ public final class LegacySQLiteStore: LocalStorage, DefaultInitializableStore { } - // MARK: Internal - - internal static let defaultRootDirectory: URL = cs_lazy { - - #if os(tvOS) - let systemDirectorySearchPath = FileManager.SearchPathDirectory.cachesDirectory - #else - let systemDirectorySearchPath = FileManager.SearchPathDirectory.applicationSupportDirectory - #endif - - return FileManager.default.urls( - for: systemDirectorySearchPath, - in: .userDomainMask).first! - } - - internal static let defaultFileURL = LegacySQLiteStore.defaultRootDirectory - .appendingPathComponent(DataStack.applicationName, isDirectory: false) - .appendingPathExtension("sqlite") - - // MARK: Private private weak var dataStack: DataStack? diff --git a/Sources/Setup/StorageInterfaces/SQLiteStore.swift b/Sources/Setup/StorageInterfaces/SQLiteStore.swift index b386e72..ca1b575 100644 --- a/Sources/Setup/StorageInterfaces/SQLiteStore.swift +++ b/Sources/Setup/StorageInterfaces/SQLiteStore.swift @@ -33,21 +33,21 @@ import CoreData - 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 final class SQLiteStore: LocalStorage, DefaultInitializableStore { +public final class SQLiteStore: LocalStorage { /** Initializes an SQLite store interface from the given SQLite file URL. When this instance is passed to the `DataStack`'s `addStorage()` methods, a new SQLite file will be created if it does not exist. - 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 mappingModelBundles: a list of `NSBundle`s from which to search mapping models (*.xcmappingmodel) for migration. + - parameter migrationMappingProviders: An array of `SchemaMappingProviders` that provides the complete mapping models for custom migrations. All lightweight inferred mappings and/or migration mappings provided by *xcmappingmodel files are automatically used as fallback (as `InferredSchemaMappingProvider`) and may be omitted from the array. - 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 init(fileURL: URL, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) { + public init(fileURL: URL, configuration: ModelConfiguration = nil, migrationMappingProviders: [SchemaMappingProvider] = [], localStorageOptions: LocalStorageOptions = nil) { self.fileURL = fileURL self.configuration = configuration - self.mappingModelBundles = mappingModelBundles + self.migrationMappingProviders = migrationMappingProviders self.localStorageOptions = localStorageOptions } @@ -57,21 +57,18 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore { - 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`. - 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 mappingModelBundles: a list of `NSBundle`s from which to search mapping models (*.xcmappingmodel) for migration + - parameter migrationMappingProviders: An array of `SchemaMappingProviders` that provides the complete mapping models for custom migrations. All lightweight inferred mappings and/or migration mappings provided by *xcmappingmodel files are automatically used as fallback (as `InferredSchemaMappingProvider`) and may be omitted from the array. - 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 init(fileName: String, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle] = Bundle.allBundles, localStorageOptions: LocalStorageOptions = nil) { + public init(fileName: String, configuration: ModelConfiguration = nil, migrationMappingProviders: [SchemaMappingProvider] = [], localStorageOptions: LocalStorageOptions = nil) { self.fileURL = SQLiteStore.defaultRootDirectory .appendingPathComponent(fileName, isDirectory: false) self.configuration = configuration - self.mappingModelBundles = mappingModelBundles + self.migrationMappingProviders = migrationMappingProviders self.localStorageOptions = localStorageOptions } - - // MARK: DefaultInitializableStore - /** Initializes an `SQLiteStore` with an all-default settings: a `fileURL` pointing to a ".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`. @@ -81,10 +78,45 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore { self.fileURL = SQLiteStore.defaultFileURL self.configuration = nil - self.mappingModelBundles = Bundle.allBundles + self.migrationMappingProviders = [] self.localStorageOptions = nil } + /** + Initializes an SQLite store interface from the given SQLite file name. When this instance is passed to the `DataStack`'s `addStorage()` methods, a new SQLite file will be created if it does not exist. + + - 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`. + - parameter legacyFileName: 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 migrationMappingProviders: An array of `SchemaMappingProviders` that provides the complete mapping models for custom migrations. All lightweight inferred mappings and/or migration mappings provided by *xcmappingmodel files are automatically used as fallback (as `InferredSchemaMappingProvider`) and may be omitted from the array. + - 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 static func legacy(fileName: String, configuration: ModelConfiguration = nil, migrationMappingProviders: [SchemaMappingProvider] = [], localStorageOptions: LocalStorageOptions = nil) -> SQLiteStore { + + return SQLiteStore( + fileURL: SQLiteStore.legacyDefaultRootDirectory + .appendingPathComponent(fileName, isDirectory: false), + configuration: configuration, + migrationMappingProviders: migrationMappingProviders, + localStorageOptions: localStorageOptions + ) + } + + /** + Initializes an `LegacySQLiteStore` with an all-default settings: a `fileURL` pointing to a ".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`. + */ + public static func legacy() -> SQLiteStore { + + return SQLiteStore( + fileURL: SQLiteStore.legacyDefaultFileURL, + configuration: nil, + migrationMappingProviders: [], + localStorageOptions: nil + ) + } + // MARK: StorageInterface @@ -131,9 +163,9 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore { public let fileURL: URL /** - The `NSBundle`s from which to search mapping models for migrations + An array of `SchemaMappingProviders` that provides the complete mapping models for custom migrations. */ - public let mappingModelBundles: [Bundle] + public let migrationMappingProviders: [SchemaMappingProvider] /** Options that tell the `DataStack` how to setup the persistent store @@ -255,8 +287,60 @@ public final class SQLiteStore: LocalStorage, DefaultInitializableStore { ) .appendingPathExtension("sqlite") + internal static let legacyDefaultRootDirectory: URL = cs_lazy { + + #if os(tvOS) + let systemDirectorySearchPath = FileManager.SearchPathDirectory.cachesDirectory + #else + let systemDirectorySearchPath = FileManager.SearchPathDirectory.applicationSupportDirectory + #endif + + return FileManager.default.urls( + for: systemDirectorySearchPath, + in: .userDomainMask).first! + } + + internal static let legacyDefaultFileURL = cs_lazy { + + return SQLiteStore.legacyDefaultRootDirectory + .appendingPathComponent(DataStack.applicationName, isDirectory: false) + .appendingPathExtension("sqlite") + } + // MARK: Private private weak var dataStack: DataStack? + + + // MARK: Deprecated + + /** + Initializes an SQLite store interface from the given SQLite file URL. When this instance is passed to the `DataStack`'s `addStorage()` methods, a new SQLite file will be created if it does not exist. + + - 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 mappingModelBundles: a list of `NSBundle`s from which to search mapping models (*.xcmappingmodel) for migration. + - 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`. + */ + @available(*, deprecated: 3.1, message: "The `mappingModelBundles` argument of this method is ignored. Use the new SQLiteStore.init(fileURL:configuration:migrationMappingProviders:localStorageOptions:) initializer instead.") + public convenience init(fileURL: URL, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle], localStorageOptions: LocalStorageOptions = nil) { + + self.init(fileURL: fileURL, configuration: configuration, migrationMappingProviders: [], localStorageOptions: localStorageOptions) + } + + /** + Initializes an SQLite store interface from the given SQLite file name. When this instance is passed to the `DataStack`'s `addStorage()` methods, a new SQLite file will be created if it does not exist. + + - 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`. + - 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 mappingModelBundles: a list of `NSBundle`s from which to search mapping models (*.xcmappingmodel) for migration. + - 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`. + */ + @available(*, deprecated: 3.1, message: "The `mappingModelBundles` argument of this method is ignored. Use the new SQLiteStore.init(fileName:configuration:migrationMappingProviders:localStorageOptions:) initializer instead.") + public convenience init(fileName: String, configuration: ModelConfiguration = nil, mappingModelBundles: [Bundle], localStorageOptions: LocalStorageOptions = nil) { + + self.init(fileName: fileName, configuration: configuration, migrationMappingProviders: [], localStorageOptions: localStorageOptions) + } } diff --git a/Sources/Setup/StorageInterfaces/StorageInterface.swift b/Sources/Setup/StorageInterfaces/StorageInterface.swift index 6775d99..f014c08 100644 --- a/Sources/Setup/StorageInterfaces/StorageInterface.swift +++ b/Sources/Setup/StorageInterfaces/StorageInterface.swift @@ -63,20 +63,6 @@ public protocol StorageInterface: class { } -// MARK: - DefaultInitializableStore - -/** - The `DefaultInitializableStore` represents `StorageInterface`s that can be initialized with default values - */ -public protocol DefaultInitializableStore: StorageInterface { - - /** - Initializes the `StorageInterface` with the default configurations - */ - init() -} - - // MARK: - LocalStorageOptions /** @@ -141,9 +127,9 @@ public protocol LocalStorage: StorageInterface { var fileURL: URL { get } /** - The `NSBundle`s from which to search mapping models (*.xcmappingmodel) for migrations + An array of `SchemaMappingProvider`s that provides the complete mapping models for custom migrations. */ - var mappingModelBundles: [Bundle] { get } + var migrationMappingProviders: [SchemaMappingProvider] { get } /** Options that tell the `DataStack` how to setup the persistent store