mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-12 20:30:30 +01:00
Compare commits
59 Commits
temp/devel
...
4.1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85706a3c57 | ||
|
|
c5ae4606b9 | ||
|
|
fa682215c5 | ||
|
|
dc73cd6dd9 | ||
|
|
f436b26e8e | ||
|
|
3ebc44b546 | ||
|
|
68f1027ba7 | ||
|
|
211e69023e | ||
|
|
2912dcf010 | ||
|
|
3e00a3da06 | ||
|
|
86be046c9f | ||
|
|
0f10bc3349 | ||
|
|
9685f0aef2 | ||
|
|
3bd459bb1a | ||
|
|
28b43f33fa | ||
|
|
a366bcf1a3 | ||
|
|
c6e68ac24f | ||
|
|
a11915db12 | ||
|
|
961f39a806 | ||
|
|
3096cb784c | ||
|
|
809aa4ff96 | ||
|
|
8d926d25ec | ||
|
|
790454f514 | ||
|
|
f72efc80b2 | ||
|
|
e8eb309d82 | ||
|
|
d0c3203e63 | ||
|
|
f5b3901caa | ||
|
|
1a99fea820 | ||
|
|
195b60615b | ||
|
|
2c394965b8 | ||
|
|
746d697691 | ||
|
|
5689158b43 | ||
|
|
6fcdf3d011 | ||
|
|
e26573c18e | ||
|
|
801cf8d9f0 | ||
|
|
8c437e19b8 | ||
|
|
9cd3b6c879 | ||
|
|
fe135acbec | ||
|
|
997c5bdcfa | ||
|
|
129f975d96 | ||
|
|
a2e463e58c | ||
|
|
5fd50f0e15 | ||
|
|
0a81736b7a | ||
|
|
f9b6dd0c6a | ||
|
|
0354401b56 | ||
|
|
0304067beb | ||
|
|
ddd83da434 | ||
|
|
fc7df671de | ||
|
|
5fde9030c7 | ||
|
|
d7b07b3f00 | ||
|
|
ddd1ffb29f | ||
|
|
98094000bb | ||
|
|
d5026ef996 | ||
|
|
2cd913b9dd | ||
|
|
ad9520abbc | ||
|
|
c0fc57d10c | ||
|
|
0cf4d303e4 | ||
|
|
6de397958a | ||
|
|
55292a84dc |
2
.cocoapods.yml
Normal file
2
.cocoapods.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
try:
|
||||
project: 'CoreStore.xcworkspace'
|
||||
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "CoreStore"
|
||||
s.version = "4.0.1"
|
||||
s.version = "4.1.3"
|
||||
s.license = "MIT"
|
||||
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
|
||||
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
||||
|
||||
@@ -238,6 +238,14 @@
|
||||
B538BA781D15B3E30003A766 /* CoreStoreBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = B538BA701D15B3E30003A766 /* CoreStoreBridge.m */; };
|
||||
B538BA791D15B3E30003A766 /* CoreStoreBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = B538BA701D15B3E30003A766 /* CoreStoreBridge.m */; };
|
||||
B538BA7A1D15B3E30003A766 /* CoreStoreBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = B538BA701D15B3E30003A766 /* CoreStoreBridge.m */; };
|
||||
B53B275F1EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; };
|
||||
B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; };
|
||||
B53B27611EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; };
|
||||
B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */; };
|
||||
B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; };
|
||||
B53CA9A31EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; };
|
||||
B53CA9A41EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; };
|
||||
B53CA9A51EF1EF1600E0F440 /* PartialObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */; };
|
||||
B53FB9FE1CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; };
|
||||
B53FBA001CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; };
|
||||
B53FBA011CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; };
|
||||
@@ -441,6 +449,18 @@
|
||||
B580857A1CDF808C004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; };
|
||||
B580857B1CDF808D004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; };
|
||||
B580857C1CDF808F004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; };
|
||||
B5831B701F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; };
|
||||
B5831B711F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; };
|
||||
B5831B721F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; };
|
||||
B5831B731F34AC3400A9F647 /* AttributeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */; };
|
||||
B5831B751F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */; };
|
||||
B5831B761F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */; };
|
||||
B5831B771F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */; };
|
||||
B5831B781F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */; };
|
||||
B5831B7A1F34ACBA00A9F647 /* Transformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B791F34ACBA00A9F647 /* Transformable.swift */; };
|
||||
B5831B7B1F34ACBA00A9F647 /* Transformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B791F34ACBA00A9F647 /* Transformable.swift */; };
|
||||
B5831B7C1F34ACBA00A9F647 /* Transformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B791F34ACBA00A9F647 /* Transformable.swift */; };
|
||||
B5831B7D1F34ACBA00A9F647 /* Transformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5831B791F34ACBA00A9F647 /* Transformable.swift */; };
|
||||
B58B22F51C93C1BA00521925 /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F03A53019C5C6DA005002A5 /* CoreStore.framework */; };
|
||||
B58D0C631EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */; };
|
||||
B58D0C641EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */; };
|
||||
@@ -752,6 +772,8 @@
|
||||
B52FD3A91E3B3EF10001D919 /* NSManagedObject+Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+Logging.swift"; sourceTree = "<group>"; };
|
||||
B533C4DA1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+CoreStore.swift"; sourceTree = "<group>"; };
|
||||
B538BA701D15B3E30003A766 /* CoreStoreBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CoreStoreBridge.m; sourceTree = "<group>"; };
|
||||
B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreManagedObject.swift; sourceTree = "<group>"; };
|
||||
B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PartialObject.swift; sourceTree = "<group>"; };
|
||||
B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSMigrationResult.swift; sourceTree = "<group>"; };
|
||||
B53FBA031CAB300C00F0D40A /* CSMigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSMigrationType.swift; sourceTree = "<group>"; };
|
||||
B53FBA0A1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSCoreStore+Migrating.swift"; sourceTree = "<group>"; };
|
||||
@@ -806,6 +828,9 @@
|
||||
B57D27BD1D0BBE8200539C58 /* BaseTestDataTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTestDataTestCase.swift; sourceTree = "<group>"; };
|
||||
B57D27C11D0BC20100539C58 /* QueryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryTests.swift; sourceTree = "<group>"; };
|
||||
B58085741CDF7F00004C2EEB /* SetupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetupTests.swift; sourceTree = "<group>"; };
|
||||
B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributeProtocol.swift; sourceTree = "<group>"; };
|
||||
B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationshipProtocol.swift; sourceTree = "<group>"; };
|
||||
B5831B791F34ACBA00A9F647 /* Transformable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformable.swift; sourceTree = "<group>"; };
|
||||
B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+DynamicModel.swift"; sourceTree = "<group>"; };
|
||||
B596BBAD1DD59FDB001DCDD9 /* ConvenienceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConvenienceTests.swift; sourceTree = "<group>"; };
|
||||
B596BBB51DD5BC67001DCDD9 /* FetchableSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchableSource.swift; sourceTree = "<group>"; };
|
||||
@@ -823,6 +848,7 @@
|
||||
B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeDataTransaction+Observing.swift"; sourceTree = "<group>"; };
|
||||
B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreFetchedResultsController.swift; sourceTree = "<group>"; };
|
||||
B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreError.swift; sourceTree = "<group>"; };
|
||||
B5D2D5A91F7558CB00A4DE67 /* .cocoapods.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = .cocoapods.yml; sourceTree = SOURCE_ROOT; };
|
||||
B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicModelTests.swift; sourceTree = "<group>"; };
|
||||
B5D339D71E9489AB00C880DE /* CoreStoreObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreObject.swift; sourceTree = "<group>"; };
|
||||
B5D339DC1E9489C700C880DE /* DynamicObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicObject.swift; sourceTree = "<group>"; };
|
||||
@@ -1019,6 +1045,7 @@
|
||||
B5E84ED91AFF82360064E85B /* LICENSE */,
|
||||
B5D9C8F61B160ED200E64F0E /* CoreStore.podspec */,
|
||||
B5BDC91A1C202269008147CD /* Cartfile */,
|
||||
B5D2D5A91F7558CB00A4DE67 /* .cocoapods.yml */,
|
||||
B5BDC9271C2024F2008147CD /* .travis.yml */,
|
||||
B5AD60CD1C90141E00F2B2E8 /* Package.swift */,
|
||||
);
|
||||
@@ -1224,17 +1251,27 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5D339D71E9489AB00C880DE /* CoreStoreObject.swift */,
|
||||
B53CA9A11EF1EF1600E0F440 /* PartialObject.swift */,
|
||||
B5831B6E1F3355C300A9F647 /* Properties */,
|
||||
B52F74391E9B8724005F3DAC /* Dynamic Schema */,
|
||||
B5D339DC1E9489C700C880DE /* DynamicObject.swift */,
|
||||
B52F742E1E9B50D0005F3DAC /* SchemaHistory.swift */,
|
||||
B5D339E61E9493A500C880DE /* Entity.swift */,
|
||||
B5D33A001E96012400C880DE /* Relationship.swift */,
|
||||
B5D339E11E948C3600C880DE /* Value.swift */,
|
||||
B5A991EB1E9DC2CE0091A2E3 /* VersionLock.swift */,
|
||||
);
|
||||
name = "Dynamic Models";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5831B6E1F3355C300A9F647 /* Properties */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5D33A001E96012400C880DE /* Relationship.swift */,
|
||||
B5D339E11E948C3600C880DE /* Value.swift */,
|
||||
B5831B791F34ACBA00A9F647 /* Transformable.swift */,
|
||||
);
|
||||
name = Properties;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B5A5F26B1CAFF8D0004AB9AF /* Swift */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1423,18 +1460,21 @@
|
||||
B5E84F291AFF849C0064E85B /* Internal */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */,
|
||||
B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */,
|
||||
B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */,
|
||||
B526613F1CADD585007B85D9 /* CoreStoreFetchRequest+CoreStore.swift */,
|
||||
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */,
|
||||
B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */,
|
||||
B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */,
|
||||
B533C4DA1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift */,
|
||||
B51260921E9B28F100402229 /* EntityIdentifier.swift */,
|
||||
B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */,
|
||||
B5E834BA1B7691F3001D3D50 /* Functions.swift */,
|
||||
B5FAD6AB1B51285300714891 /* MigrationManager.swift */,
|
||||
B5E84F2B1AFF849C0064E85B /* NotificationObserver.swift */,
|
||||
B533C4DA1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift */,
|
||||
B52FD3A91E3B3EF10001D919 /* NSManagedObject+Logging.swift */,
|
||||
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */,
|
||||
B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */,
|
||||
B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */,
|
||||
B52FD3A91E3B3EF10001D919 /* NSManagedObject+Logging.swift */,
|
||||
B5E84F2C1AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift */,
|
||||
B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */,
|
||||
B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */,
|
||||
@@ -1802,6 +1842,7 @@
|
||||
B5ECDC1D1CA81A2100C7F112 /* CSDataStack+Querying.swift in Sources */,
|
||||
B5C976E31C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */,
|
||||
B53FBA121CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
||||
B5831B751F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B5E1B5A81CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
||||
B5D339F11E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
B56007161B4018AB00A9A8F9 /* MigrationChain.swift in Sources */,
|
||||
@@ -1820,6 +1861,7 @@
|
||||
B5FAD6AE1B518DCB00714891 /* CoreStore+Migration.swift in Sources */,
|
||||
B5E84EE71AFF84610064E85B /* CoreStore+Logging.swift in Sources */,
|
||||
B546F9731C9C553300D5AC55 /* SetupResult.swift in Sources */,
|
||||
B53CA9A21EF1EF1600E0F440 /* PartialObject.swift in Sources */,
|
||||
B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */,
|
||||
B5E84F111AFF847B0064E85B /* Select.swift in Sources */,
|
||||
B51260931E9B28F100402229 /* EntityIdentifier.swift in Sources */,
|
||||
@@ -1875,6 +1917,7 @@
|
||||
B5E2222A1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */,
|
||||
B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */,
|
||||
B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||
B53B275F1EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||
B5D33A011E96012400C880DE /* Relationship.swift in Sources */,
|
||||
B5E84EE81AFF84610064E85B /* CoreStoreLogger.swift in Sources */,
|
||||
B56923C91EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
||||
@@ -1892,6 +1935,7 @@
|
||||
B59FA0AE1CCBAC95007C9BCA /* ICloudStore.swift in Sources */,
|
||||
B5E84EF81AFF846E0064E85B /* CoreStore+Transaction.swift in Sources */,
|
||||
B5E84F301AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||
B5831B7A1F34ACBA00A9F647 /* Transformable.swift in Sources */,
|
||||
B546F9691C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||
B53FBA1E1CAB63FA00F0D40A /* NSFetchedResultsController+ObjectiveC.swift in Sources */,
|
||||
B549F65E1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */,
|
||||
@@ -1907,6 +1951,7 @@
|
||||
B5E84EE61AFF84610064E85B /* DefaultLogger.swift in Sources */,
|
||||
B53FBA041CAB300C00F0D40A /* CSMigrationType.swift in Sources */,
|
||||
B5E84EF41AFF846E0064E85B /* AsynchronousDataTransaction.swift in Sources */,
|
||||
B5831B701F34AC3400A9F647 /* AttributeProtocol.swift in Sources */,
|
||||
B5DBE2CD1C9914A900B5CEFA /* CSCoreStore.swift in Sources */,
|
||||
B546F95D1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */,
|
||||
B5D339E71E9493A500C880DE /* Entity.swift in Sources */,
|
||||
@@ -1986,6 +2031,7 @@
|
||||
B5ECDC1F1CA81A2100C7F112 /* CSDataStack+Querying.swift in Sources */,
|
||||
B5C976E41C6C9F9A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */,
|
||||
B53FBA141CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
||||
B5831B761F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B5E1B5AA1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
||||
B5D339F21E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
B5D3F6461C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */,
|
||||
@@ -2004,6 +2050,7 @@
|
||||
82BA18A31C4BBD2200A0916E /* DataStack.swift in Sources */,
|
||||
82BA18C81C4BBD5900A0916E /* MigrationChain.swift in Sources */,
|
||||
B546F9741C9C553300D5AC55 /* SetupResult.swift in Sources */,
|
||||
B53CA9A31EF1EF1600E0F440 /* PartialObject.swift in Sources */,
|
||||
82BA18B11C4BBD3100A0916E /* SaveResult.swift in Sources */,
|
||||
82BA18DD1C4BBE1400A0916E /* NSFetchedResultsController+Convenience.swift in Sources */,
|
||||
B51260941E9B28F100402229 /* EntityIdentifier.swift in Sources */,
|
||||
@@ -2059,6 +2106,7 @@
|
||||
82BA18A71C4BBD2900A0916E /* CoreStore+Logging.swift in Sources */,
|
||||
82BA18D81C4BBD7100A0916E /* WeakObject.swift in Sources */,
|
||||
B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||
B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||
B5D33A021E96012400C880DE /* Relationship.swift in Sources */,
|
||||
B559CD4B1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */,
|
||||
B56923CA1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
||||
@@ -2076,6 +2124,7 @@
|
||||
82BA18D31C4BBD7100A0916E /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||
82BA18AD1C4BBD3100A0916E /* UnsafeDataTransaction.swift in Sources */,
|
||||
B546F96A1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||
B5831B7B1F34ACBA00A9F647 /* Transformable.swift in Sources */,
|
||||
B53FBA201CAB63FA00F0D40A /* NSFetchedResultsController+ObjectiveC.swift in Sources */,
|
||||
82BA18A81C4BBD2900A0916E /* CoreStoreLogger.swift in Sources */,
|
||||
B549F65F1E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */,
|
||||
@@ -2091,6 +2140,7 @@
|
||||
B53FBA061CAB300C00F0D40A /* CSMigrationType.swift in Sources */,
|
||||
82BA18BE1C4BBD4A00A0916E /* Tweak.swift in Sources */,
|
||||
B5DBE2CE1C9914A900B5CEFA /* CSCoreStore.swift in Sources */,
|
||||
B5831B711F34AC3400A9F647 /* AttributeProtocol.swift in Sources */,
|
||||
B546F95E1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */,
|
||||
B5ECDC0D1CA8161B00C7F112 /* CSGroupBy.swift in Sources */,
|
||||
B5D339E81E9493A500C880DE /* Entity.swift in Sources */,
|
||||
@@ -2170,6 +2220,7 @@
|
||||
B5D3F6481C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */,
|
||||
B5220E1C1D130801009BC71E /* FetchedResultsControllerDelegate.swift in Sources */,
|
||||
B52DD19E1BE1F92C00949AFE /* AsynchronousDataTransaction.swift in Sources */,
|
||||
B5831B781F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B52DD1981BE1F92500949AFE /* CoreStore+Setup.swift in Sources */,
|
||||
B5D339F41E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
B5220E241D13085E009BC71E /* NSFetchedResultsController+Convenience.swift in Sources */,
|
||||
@@ -2188,6 +2239,7 @@
|
||||
B52DD1961BE1F92500949AFE /* DataStack.swift in Sources */,
|
||||
B5ECDBFD1CA804FD00C7F112 /* NSManagedObjectContext+ObjectiveC.swift in Sources */,
|
||||
B52DD1BD1BE1F94300949AFE /* NSManagedObject+Convenience.swift in Sources */,
|
||||
B53CA9A51EF1EF1600E0F440 /* PartialObject.swift in Sources */,
|
||||
B52DD1AD1BE1F93900949AFE /* Where.swift in Sources */,
|
||||
B53FBA1C1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */,
|
||||
B51260961E9B28F100402229 /* EntityIdentifier.swift in Sources */,
|
||||
@@ -2243,6 +2295,7 @@
|
||||
B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */,
|
||||
B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||
B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||
B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||
B5D33A041E96012400C880DE /* Relationship.swift in Sources */,
|
||||
B52DD1C61BE1F94600949AFE /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||
B56923CC1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
||||
@@ -2260,6 +2313,7 @@
|
||||
B52DD1A01BE1F92C00949AFE /* UnsafeDataTransaction.swift in Sources */,
|
||||
B5ECDC331CA81CDC00C7F112 /* CSCoreStore+Transaction.swift in Sources */,
|
||||
B52DD1BB1BE1F94000949AFE /* MigrationType.swift in Sources */,
|
||||
B5831B7D1F34ACBA00A9F647 /* Transformable.swift in Sources */,
|
||||
B52DD1C91BE1F94600949AFE /* NSManagedObjectContext+Transaction.swift in Sources */,
|
||||
B5220E151D130663009BC71E /* CoreStore+Observing.swift in Sources */,
|
||||
B549F6611E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */,
|
||||
@@ -2275,6 +2329,7 @@
|
||||
B52DD1B91BE1F94000949AFE /* CoreStore+Migration.swift in Sources */,
|
||||
B5519A5C1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */,
|
||||
B5DBE2D51C991B3E00B5CEFA /* CSDataStack.swift in Sources */,
|
||||
B5831B731F34AC3400A9F647 /* AttributeProtocol.swift in Sources */,
|
||||
B5AEFAB81C9962AE00AD137F /* CoreStoreBridge.swift in Sources */,
|
||||
B598514B1C90289F00C99590 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
|
||||
B5D339EA1E9493A500C880DE /* Entity.swift in Sources */,
|
||||
@@ -2354,6 +2409,7 @@
|
||||
B5C976E51C6C9F9B00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */,
|
||||
B53FBA151CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
||||
B5E1B5AB1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
||||
B5831B771F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B5D3F6471C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */,
|
||||
B5D339F31E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
B5E1B5A01CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */,
|
||||
@@ -2372,6 +2428,7 @@
|
||||
B56321A81BD65219006C9394 /* NSManagedObject+Convenience.swift in Sources */,
|
||||
B546F9751C9C553300D5AC55 /* SetupResult.swift in Sources */,
|
||||
B56321981BD65216006C9394 /* Where.swift in Sources */,
|
||||
B53CA9A41EF1EF1600E0F440 /* PartialObject.swift in Sources */,
|
||||
B5202CFD1C046E8400DED140 /* NSFetchedResultsController+Convenience.swift in Sources */,
|
||||
B5FE4DA91C84FB4400FA6A91 /* InMemoryStore.swift in Sources */,
|
||||
B51260951E9B28F100402229 /* EntityIdentifier.swift in Sources */,
|
||||
@@ -2427,6 +2484,7 @@
|
||||
B563219F1BD65216006C9394 /* ObjectMonitor.swift in Sources */,
|
||||
B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */,
|
||||
B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||
B53B27611EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||
B5D33A031E96012400C880DE /* Relationship.swift in Sources */,
|
||||
B559CD4C1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */,
|
||||
B56923CB1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
||||
@@ -2444,6 +2502,7 @@
|
||||
B56321B11BD6521C006C9394 /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||
B563218D1BD65216006C9394 /* CoreStore+Transaction.swift in Sources */,
|
||||
B546F96B1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||
B5831B7C1F34ACBA00A9F647 /* Transformable.swift in Sources */,
|
||||
B53FBA211CAB63FA00F0D40A /* NSFetchedResultsController+ObjectiveC.swift in Sources */,
|
||||
B563218B1BD65216006C9394 /* UnsafeDataTransaction.swift in Sources */,
|
||||
B549F6601E569C7400FBAB2D /* QueryableAttributeType.swift in Sources */,
|
||||
@@ -2459,6 +2518,7 @@
|
||||
B53FBA071CAB300C00F0D40A /* CSMigrationType.swift in Sources */,
|
||||
B56321841BD65216006C9394 /* DefaultLogger.swift in Sources */,
|
||||
B5DBE2CF1C9914A900B5CEFA /* CSCoreStore.swift in Sources */,
|
||||
B5831B721F34AC3400A9F647 /* AttributeProtocol.swift in Sources */,
|
||||
B546F95F1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */,
|
||||
B5ECDC0E1CA8161B00C7F112 /* CSGroupBy.swift in Sources */,
|
||||
B5D339E91E9493A500C880DE /* Entity.swift in Sources */,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12113" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="Ni8-QF-XHB">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="Ni8-QF-XHB">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12078"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
@@ -459,8 +459,8 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="301.5"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" verticalCompressionResistancePriority="250" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NhC-oM-bkd">
|
||||
<rect key="frame" x="20" y="69.5" width="552" height="36.5"/>
|
||||
<view contentMode="scaleToFill" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="NhC-oM-bkd">
|
||||
<rect key="frame" x="16" y="89.5" width="343" height="50"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" id="TIX-qi-B34"/>
|
||||
@@ -511,8 +511,8 @@
|
||||
<action selector="brightnessSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="F09-EP-2iD"/>
|
||||
</connections>
|
||||
</slider>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p4O-tf-dgt">
|
||||
<rect key="frame" x="20" y="49" width="552" height="20.5"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p4O-tf-dgt">
|
||||
<rect key="frame" x="16" y="69" width="343" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -797,8 +797,8 @@
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="V2U-0R-Ts0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="536"/>
|
||||
<mapView verifyAmbiguity="ignoreSizes" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="V2U-0R-Ts0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="558"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="jPl-fH-NlD" id="Sjn-YC-haS"/>
|
||||
</connections>
|
||||
@@ -806,7 +806,7 @@
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="RZg-hi-T8O" firstAttribute="top" secondItem="V2U-0R-Ts0" secondAttribute="bottom" id="GcS-Jz-Wcm"/>
|
||||
<constraint firstItem="RZg-hi-T8O" firstAttribute="top" secondItem="V2U-0R-Ts0" secondAttribute="bottom" id="N9r-9J-68d"/>
|
||||
<constraint firstItem="V2U-0R-Ts0" firstAttribute="top" secondItem="k4s-iL-Krh" secondAttribute="top" id="S5Z-Da-V6J"/>
|
||||
<constraint firstAttribute="trailing" secondItem="V2U-0R-Ts0" secondAttribute="trailing" id="YPc-RK-5ib"/>
|
||||
<constraint firstItem="V2U-0R-Ts0" firstAttribute="leading" secondItem="k4s-iL-Krh" secondAttribute="leading" id="hk5-Rz-FyU"/>
|
||||
@@ -820,7 +820,7 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="YnG-TD-zxQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3694" y="2020"/>
|
||||
<point key="canvasLocation" x="3693.5" y="2019.5"/>
|
||||
</scene>
|
||||
<!--Logger-->
|
||||
<scene sceneID="n7W-0g-bbY">
|
||||
@@ -927,7 +927,7 @@
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bordered" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="YVj-dA-fyV">
|
||||
<rect key="frame" x="20" y="26" width="560" height="29"/>
|
||||
<rect key="frame" x="20" y="26" width="335" height="29"/>
|
||||
<segments>
|
||||
<segment title="Fetch"/>
|
||||
<segment title="Query"/>
|
||||
|
||||
@@ -1,36 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10169.1" systemVersion="15D21" minimumToolsVersion="Automatic">
|
||||
<entity name="Palette" representedClassName="CoreStoreDemo.Palette">
|
||||
<attribute name="brightness" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="colorName" optional="YES" transient="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="hue" optional="YES" attributeType="Integer 32" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="saturation" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
|
||||
<userInfo/>
|
||||
</entity>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12141" systemVersion="16F73" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Place" representedClassName="CoreStoreDemo.Place" syncable="YES">
|
||||
<attribute name="latitude" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="latitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="subtitle" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="TimeZone" representedClassName="CoreStoreDemo.TimeZone" syncable="YES">
|
||||
<attribute name="abbreviation" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="daylightSavingTimeOffset" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="hasDaylightSavingTime" optional="YES" attributeType="Boolean" syncable="YES"/>
|
||||
<attribute name="daylightSavingTimeOffset" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="hasDaylightSavingTime" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="secondsFromGMT" optional="YES" attributeType="Integer 32" defaultValueString="0.0" syncable="YES"/>
|
||||
<attribute name="secondsFromGMT" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="NO" syncable="YES"/>
|
||||
</entity>
|
||||
<configuration name="FetchingAndQueryingDemo">
|
||||
<memberEntity name="TimeZone"/>
|
||||
</configuration>
|
||||
<configuration name="ObservingDemo">
|
||||
<memberEntity name="Palette"/>
|
||||
</configuration>
|
||||
<configuration name="TransactionsDemo">
|
||||
<memberEntity name="Place"/>
|
||||
</configuration>
|
||||
<elements>
|
||||
<element name="Palette" positionX="261" positionY="189" width="128" height="105"/>
|
||||
<element name="Place" positionX="261" positionY="225" width="128" height="105"/>
|
||||
<element name="TimeZone" positionX="297" positionY="270" width="128" height="120"/>
|
||||
</elements>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.0.1</string>
|
||||
<string>4.0.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -10,7 +10,7 @@ import UIKit
|
||||
import CoreStore
|
||||
|
||||
|
||||
private struct Static {
|
||||
struct ColorsDemo {
|
||||
|
||||
enum Filter: String {
|
||||
|
||||
@@ -33,8 +33,8 @@ private struct Static {
|
||||
switch self {
|
||||
|
||||
case .all: return Where(true)
|
||||
case .light: return Where("%K >= %@", #keyPath(Palette.brightness), 0.9)
|
||||
case .dark: return Where("%K <= %@", #keyPath(Palette.brightness), 0.4)
|
||||
case .light: return Palette.where({ $0.brightness >= 0.9 })
|
||||
case .dark: return Palette.where({ $0.brightness <= 0.4 })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,25 +45,38 @@ private struct Static {
|
||||
|
||||
self.palettes.refetch(
|
||||
self.filter.whereClause(),
|
||||
OrderBy(.ascending(#keyPath(Palette.hue)))
|
||||
Palette.orderBy(ascending: { $0.hue })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static let stack: DataStack = {
|
||||
|
||||
return DataStack(
|
||||
CoreStoreSchema(
|
||||
modelVersion: "ColorsDemo",
|
||||
entities: [
|
||||
Entity<Palette>("Palette"),
|
||||
],
|
||||
versionLock: [
|
||||
"Palette": [0x8c25aa53c7c90a28, 0xa243a34d25f1a3a7, 0x56565b6935b6055a, 0x4f988bb257bf274f]
|
||||
]
|
||||
)
|
||||
)
|
||||
}()
|
||||
|
||||
static let palettes: ListMonitor<Palette> = {
|
||||
|
||||
try! CoreStore.addStorageAndWait(
|
||||
try! ColorsDemo.stack.addStorageAndWait(
|
||||
SQLiteStore(
|
||||
fileName: "ColorsDemo.sqlite",
|
||||
configuration: "ObservingDemo",
|
||||
localStorageOptions: .recreateStoreOnModelMismatch
|
||||
)
|
||||
)
|
||||
|
||||
return CoreStore.monitorSectionedList(
|
||||
return ColorsDemo.stack.monitorSectionedList(
|
||||
From<Palette>(),
|
||||
SectionBy(#keyPath(Palette.colorName)),
|
||||
OrderBy(.ascending(#keyPath(Palette.hue)))
|
||||
SectionBy(Palette.keyPath({ $0.colorName })),
|
||||
Palette.orderBy(ascending: { $0.hue })
|
||||
)
|
||||
}()
|
||||
}
|
||||
@@ -77,7 +90,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
|
||||
deinit {
|
||||
|
||||
Static.palettes.removeObserver(self)
|
||||
ColorsDemo.palettes.removeObserver(self)
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +111,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
]
|
||||
|
||||
let filterBarButton = UIBarButtonItem(
|
||||
title: Static.filter.rawValue,
|
||||
title: ColorsDemo.filter.rawValue,
|
||||
style: .plain,
|
||||
target: self,
|
||||
action: #selector(self.filterBarButtonItemTouched(_:))
|
||||
@@ -113,9 +126,9 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
]
|
||||
self.filterBarButton = filterBarButton
|
||||
|
||||
Static.palettes.addObserver(self)
|
||||
ColorsDemo.palettes.addObserver(self)
|
||||
|
||||
self.setTable(enabled: !Static.palettes.isPendingRefetch)
|
||||
self.setTable(enabled: !ColorsDemo.palettes.isPendingRefetch)
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
@@ -137,19 +150,19 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
|
||||
return Static.palettes.numberOfSections()
|
||||
return ColorsDemo.palettes.numberOfSections()
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
|
||||
return Static.palettes.numberOfObjectsInSection(section)
|
||||
return ColorsDemo.palettes.numberOfObjectsInSection(section)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PaletteTableViewCell") as! PaletteTableViewCell
|
||||
|
||||
let palette = Static.palettes[indexPath]
|
||||
let palette = ColorsDemo.palettes[indexPath]
|
||||
cell.colorView?.backgroundColor = palette.color
|
||||
cell.label?.text = palette.colorText
|
||||
|
||||
@@ -165,7 +178,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
|
||||
self.performSegue(
|
||||
withIdentifier: "ObjectObserverDemoViewController",
|
||||
sender: Static.palettes[indexPath]
|
||||
sender: ColorsDemo.palettes[indexPath]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -174,8 +187,8 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
switch editingStyle {
|
||||
|
||||
case .delete:
|
||||
let palette = Static.palettes[indexPath]
|
||||
CoreStore.perform(
|
||||
let palette = ColorsDemo.palettes[indexPath]
|
||||
ColorsDemo.stack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
transaction.delete(palette)
|
||||
@@ -190,7 +203,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
|
||||
return Static.palettes.sectionInfoAtIndex(section).name
|
||||
return ColorsDemo.palettes.sectionInfoAtIndex(section).name
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +226,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
|
||||
func listMonitorDidRefetch(_ monitor: ListMonitor<Palette>) {
|
||||
|
||||
self.filterBarButton?.title = Static.filter.rawValue
|
||||
self.filterBarButton?.title = ColorsDemo.filter.rawValue
|
||||
self.tableView.reloadData()
|
||||
self.setTable(enabled: true)
|
||||
}
|
||||
@@ -235,7 +248,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
|
||||
if let cell = self.tableView.cellForRow(at: indexPath) as? PaletteTableViewCell {
|
||||
|
||||
let palette = Static.palettes[indexPath]
|
||||
let palette = ColorsDemo.palettes[indexPath]
|
||||
cell.colorView?.backgroundColor = palette.color
|
||||
cell.label?.text = palette.colorText
|
||||
}
|
||||
@@ -268,7 +281,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
|
||||
@IBAction private dynamic func resetBarButtonItemTouched(_ sender: AnyObject?) {
|
||||
|
||||
CoreStore.perform(
|
||||
ColorsDemo.stack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
transaction.deleteAll(From<Palette>())
|
||||
@@ -279,16 +292,16 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
||||
|
||||
@IBAction private dynamic func filterBarButtonItemTouched(_ sender: AnyObject?) {
|
||||
|
||||
Static.filter = Static.filter.next()
|
||||
ColorsDemo.filter = ColorsDemo.filter.next()
|
||||
}
|
||||
|
||||
@IBAction private dynamic func addBarButtonItemTouched(_ sender: AnyObject?) {
|
||||
|
||||
CoreStore.perform(
|
||||
ColorsDemo.stack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let palette = transaction.create(Into<Palette>())
|
||||
palette.setInitialValues()
|
||||
palette.setInitialValues(in: transaction)
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
||||
|
||||
if let palette = newValue {
|
||||
|
||||
self.monitor = CoreStore.monitorObject(palette)
|
||||
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -50,22 +50,22 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
|
||||
if let palette = CoreStore.fetchOne(From<Palette>(), OrderBy(.ascending(#keyPath(Palette.hue)))) {
|
||||
if let palette = ColorsDemo.stack.fetchOne(From<Palette>(), Palette.orderBy(ascending: { $0.hue })) {
|
||||
|
||||
self.monitor = CoreStore.monitorObject(palette)
|
||||
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
||||
}
|
||||
else {
|
||||
|
||||
_ = try? CoreStore.perform(
|
||||
_ = try? ColorsDemo.stack.perform(
|
||||
synchronous: { (transaction) in
|
||||
|
||||
let palette = transaction.create(Into(Palette.self))
|
||||
palette.setInitialValues()
|
||||
let palette = transaction.create(Into<Palette>())
|
||||
palette.setInitialValues(in: transaction)
|
||||
}
|
||||
)
|
||||
|
||||
let palette = CoreStore.fetchOne(From<Palette>(), OrderBy(.ascending(#keyPath(Palette.hue))))!
|
||||
self.monitor = CoreStore.monitorObject(palette)
|
||||
let palette = ColorsDemo.stack.fetchOne(From<Palette>(), Palette.orderBy(ascending: { $0.hue }))!
|
||||
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
||||
}
|
||||
|
||||
super.init(coder: aDecoder)
|
||||
@@ -121,12 +121,12 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
||||
@IBAction dynamic func hueSliderValueDidChange(_ sender: AnyObject?) {
|
||||
|
||||
let hue = self.hueSlider?.value ?? 0
|
||||
CoreStore.perform(
|
||||
ColorsDemo.stack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
|
||||
if let palette = transaction.edit(self?.monitor?.object) {
|
||||
|
||||
palette.hue = Int32(hue)
|
||||
palette.hue .= Int(hue)
|
||||
}
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -136,12 +136,12 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
||||
@IBAction dynamic func saturationSliderValueDidChange(_ sender: AnyObject?) {
|
||||
|
||||
let saturation = self.saturationSlider?.value ?? 0
|
||||
CoreStore.perform(
|
||||
ColorsDemo.stack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
|
||||
if let palette = transaction.edit(self?.monitor?.object) {
|
||||
|
||||
palette.saturation = saturation
|
||||
palette.saturation .= saturation
|
||||
}
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -151,12 +151,12 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
||||
@IBAction dynamic func brightnessSliderValueDidChange(_ sender: AnyObject?) {
|
||||
|
||||
let brightness = self.brightnessSlider?.value ?? 0
|
||||
CoreStore.perform(
|
||||
ColorsDemo.stack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
|
||||
if let palette = transaction.edit(self?.monitor?.object) {
|
||||
|
||||
palette.brightness = brightness
|
||||
palette.brightness .= brightness
|
||||
}
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -165,7 +165,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
||||
|
||||
@IBAction dynamic func deleteBarButtonTapped(_ sender: AnyObject?) {
|
||||
|
||||
CoreStore.perform(
|
||||
ColorsDemo.stack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
|
||||
transaction.delete(self?.monitor?.object)
|
||||
@@ -176,7 +176,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
||||
|
||||
func reloadPaletteInfo(_ palette: Palette, changedKeys: Set<String>?) {
|
||||
|
||||
self.colorNameLabel?.text = palette.colorName
|
||||
self.colorNameLabel?.text = palette.colorName.value
|
||||
|
||||
let color = palette.color
|
||||
self.colorNameLabel?.textColor = color
|
||||
@@ -184,17 +184,17 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
||||
|
||||
self.hsbLabel?.text = palette.colorText
|
||||
|
||||
if changedKeys == nil || changedKeys?.contains(#keyPath(Palette.hue)) == true {
|
||||
if changedKeys == nil || changedKeys?.contains(Palette.keyPath{ $0.hue }) == true {
|
||||
|
||||
self.hueSlider?.value = Float(palette.hue)
|
||||
self.hueSlider?.value = Float(palette.hue.value)
|
||||
}
|
||||
if changedKeys == nil || changedKeys?.contains(#keyPath(Palette.saturation)) == true {
|
||||
if changedKeys == nil || changedKeys?.contains(Palette.keyPath{ $0.saturation }) == true {
|
||||
|
||||
self.saturationSlider?.value = palette.saturation
|
||||
self.saturationSlider?.value = palette.saturation.value
|
||||
}
|
||||
if changedKeys == nil || changedKeys?.contains(#keyPath(Palette.brightness)) == true {
|
||||
if changedKeys == nil || changedKeys?.contains(Palette.keyPath{ $0.brightness }) == true {
|
||||
|
||||
self.brightnessSlider?.value = palette.brightness
|
||||
self.brightnessSlider?.value = palette.brightness.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,64 +14,65 @@ import CoreStore
|
||||
|
||||
// MARK: - Palette
|
||||
|
||||
class Palette: NSManagedObject {
|
||||
|
||||
@NSManaged var hue: Int32
|
||||
@NSManaged var saturation: Float
|
||||
@NSManaged var brightness: Float
|
||||
final class Palette: CoreStoreObject {
|
||||
|
||||
@objc dynamic var colorName: String {
|
||||
let hue = Value.Required<Int>("hue", initial: 0)
|
||||
let saturation = Value.Required<Float>("saturation", initial: 0)
|
||||
let brightness = Value.Required<Float>("brightness", initial: 0)
|
||||
|
||||
let colorName = Value.Optional<String>(
|
||||
"colorName",
|
||||
isTransient: true,
|
||||
customGetter: Palette.getColorName
|
||||
)
|
||||
|
||||
private static func getColorName(_ partialObject: PartialObject<Palette>) -> String? {
|
||||
|
||||
get {
|
||||
if let colorName = partialObject.primitiveValue(for: { $0.colorName }) {
|
||||
|
||||
let KVCKey = #keyPath(Palette.colorName)
|
||||
if let colorName = self.getValue(forKvcKey: KVCKey) as? String {
|
||||
|
||||
return colorName
|
||||
}
|
||||
|
||||
let colorName: String
|
||||
switch self.hue % 360 {
|
||||
|
||||
case 0 ..< 20: colorName = "Lower Reds"
|
||||
case 20 ..< 57: colorName = "Oranges and Browns"
|
||||
case 57 ..< 90: colorName = "Yellow-Greens"
|
||||
case 90 ..< 159: colorName = "Greens"
|
||||
case 159 ..< 197: colorName = "Blue-Greens"
|
||||
case 197 ..< 241: colorName = "Blues"
|
||||
case 241 ..< 297: colorName = "Violets"
|
||||
case 297 ..< 331: colorName = "Magentas"
|
||||
default: colorName = "Upper Reds"
|
||||
}
|
||||
|
||||
self.setPrimitiveValue(colorName, forKey: KVCKey)
|
||||
return colorName
|
||||
}
|
||||
set {
|
||||
|
||||
let colorName: String
|
||||
switch partialObject.value(for: { $0.hue }) % 360 {
|
||||
|
||||
self.setValue(newValue.cs_toImportableNativeType(), forKvcKey: #keyPath(Palette.colorName))
|
||||
case 0 ..< 20: colorName = "Lower Reds"
|
||||
case 20 ..< 57: colorName = "Oranges and Browns"
|
||||
case 57 ..< 90: colorName = "Yellow-Greens"
|
||||
case 90 ..< 159: colorName = "Greens"
|
||||
case 159 ..< 197: colorName = "Blue-Greens"
|
||||
case 197 ..< 241: colorName = "Blues"
|
||||
case 241 ..< 297: colorName = "Violets"
|
||||
case 297 ..< 331: colorName = "Magentas"
|
||||
default: colorName = "Upper Reds"
|
||||
}
|
||||
|
||||
partialObject.setPrimitiveValue(colorName, for: { $0.colorName })
|
||||
return colorName
|
||||
}
|
||||
}
|
||||
|
||||
extension Palette {
|
||||
|
||||
var color: UIColor {
|
||||
|
||||
return UIColor(
|
||||
hue: CGFloat(self.hue) / 360.0,
|
||||
saturation: CGFloat(self.saturation),
|
||||
brightness: CGFloat(self.brightness),
|
||||
hue: CGFloat(self.hue.value) / 360.0,
|
||||
saturation: CGFloat(self.saturation.value),
|
||||
brightness: CGFloat(self.brightness.value),
|
||||
alpha: 1.0
|
||||
)
|
||||
}
|
||||
|
||||
var colorText: String {
|
||||
|
||||
return "H: \(self.hue)˚, S: \(round(self.saturation * 100.0))%, B: \(round(self.brightness * 100.0))%"
|
||||
return "H: \(self.hue.value)˚, S: \(round(self.saturation.value * 100.0))%, B: \(round(self.brightness.value * 100.0))%"
|
||||
}
|
||||
|
||||
func setInitialValues() {
|
||||
func setInitialValues(in transaction: BaseDataTransaction) {
|
||||
|
||||
self.hue = Int32(arc4random_uniform(360))
|
||||
self.saturation = 1.0
|
||||
self.brightness = Float(arc4random_uniform(70) + 30) / 100.0
|
||||
self.hue .= Int(arc4random_uniform(360))
|
||||
self.saturation .= Float(1.0)
|
||||
self.brightness .= Float(arc4random_uniform(70) + 30) / 100.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger {
|
||||
case 2?:
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
|
||||
_ = self.dataStack.fetchOne(From<Palette>())
|
||||
_ = self.dataStack.fetchOne(From<Place>())
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -46,7 +46,7 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
|
||||
|
||||
let modelMetadata = withExtendedLifetime(DataStack(modelName: "MigrationDemo")) {
|
||||
let modelMetadata = withExtendedLifetime(DataStack(xcodeModelName: "MigrationDemo")) {
|
||||
(dataStack: DataStack) -> ModelMetadata in
|
||||
|
||||
let models = self.models
|
||||
|
||||
@@ -17,7 +17,7 @@ private struct Static {
|
||||
|
||||
static let facebookStack: DataStack = {
|
||||
|
||||
let dataStack = DataStack(modelName: "StackSetupDemo")
|
||||
let dataStack = DataStack(xcodeModelName: "StackSetupDemo")
|
||||
try! dataStack.addStorageAndWait(
|
||||
SQLiteStore(
|
||||
fileName: "AccountsDemo_FB_Male.sqlite",
|
||||
@@ -55,7 +55,7 @@ private struct Static {
|
||||
|
||||
static let twitterStack: DataStack = {
|
||||
|
||||
let dataStack = DataStack(modelName: "StackSetupDemo")
|
||||
let dataStack = DataStack(xcodeModelName: "StackSetupDemo")
|
||||
try! dataStack.addStorageAndWait(
|
||||
SQLiteStore(
|
||||
fileName: "AccountsDemo_TW_Male.sqlite",
|
||||
|
||||
@@ -37,7 +37,7 @@ import CoreStore
|
||||
|
||||
class Animal: CoreStoreObject {
|
||||
|
||||
let species = Value.Required<String>("species", default: "Swift")
|
||||
let species = Value.Required<String>("species", initial: "Swift")
|
||||
let master = Relationship.ToOne<Person>("master")
|
||||
let color = Transformable.Optional<Color>("color")
|
||||
}
|
||||
@@ -45,21 +45,66 @@ class Animal: CoreStoreObject {
|
||||
class Dog: Animal {
|
||||
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let age = Value.Required<Int>("age", default: 1)
|
||||
let age = Value.Required<Int>("age", initial: 1)
|
||||
let friends = Relationship.ToManyOrdered<Dog>("friends")
|
||||
let friendedBy = Relationship.ToManyUnordered<Dog>("friendedBy", inverse: { $0.friends })
|
||||
}
|
||||
|
||||
class Person: CoreStoreObject {
|
||||
let title = Value.Required<String>("title", default: "Mr.")
|
||||
|
||||
let title = Value.Required<String>(
|
||||
"title",
|
||||
initial: "Mr.",
|
||||
customSetter: Person.setTitle
|
||||
)
|
||||
|
||||
let name = Value.Required<String>(
|
||||
"name",
|
||||
customGetter: { (`self`, getValue) in
|
||||
|
||||
return "\(self.title.value) \(getValue())"
|
||||
}
|
||||
initial: "",
|
||||
customSetter: Person.setName
|
||||
)
|
||||
|
||||
let displayName = Value.Optional<String>(
|
||||
"displayName",
|
||||
isTransient: true,
|
||||
customGetter: Person.getDisplayName(_:),
|
||||
affectedByKeyPaths: Person.keyPathsAffectingDisplayName()
|
||||
)
|
||||
|
||||
let pets = Relationship.ToManyUnordered<Animal>("pets", inverse: { $0.master })
|
||||
|
||||
private static func setTitle(_ partialObject: PartialObject<Person>, _ newValue: String) {
|
||||
|
||||
partialObject.setPrimitiveValue(newValue, for: { $0.title })
|
||||
partialObject.setPrimitiveValue(nil, for: { $0.displayName })
|
||||
}
|
||||
|
||||
private static func setName(_ partialObject: PartialObject<Person>, _ newValue: String) {
|
||||
|
||||
partialObject.setPrimitiveValue(newValue, for: { $0.name })
|
||||
partialObject.setPrimitiveValue(nil, for: { $0.displayName })
|
||||
}
|
||||
|
||||
static func getDisplayName(_ partialObject: PartialObject<Person>) -> String? {
|
||||
|
||||
if let displayName = partialObject.primitiveValue(for: { $0.displayName }) {
|
||||
|
||||
return displayName
|
||||
}
|
||||
let title = partialObject.value(for: { $0.title })
|
||||
let name = partialObject.value(for: { $0.name })
|
||||
let displayName = "\(title) \(name)"
|
||||
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
|
||||
return displayName
|
||||
}
|
||||
|
||||
static func keyPathsAffectingDisplayName() -> Set<String> {
|
||||
|
||||
return [
|
||||
self.keyPath({ $0.title }),
|
||||
self.keyPath({ $0.name })
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,11 +169,25 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
let person = transaction.create(Into<Person>())
|
||||
XCTAssertTrue(person.pets.value.isEmpty)
|
||||
|
||||
XCTAssertEqual(
|
||||
object_getClass(person.rawObject!).keyPathsForValuesAffectingValue(forKey: "displayName"),
|
||||
["title", "name"]
|
||||
)
|
||||
|
||||
person.name .= "Joe"
|
||||
|
||||
XCTAssertEqual(person.rawObject!.value(forKey: "name") as! String?, "Joe")
|
||||
XCTAssertEqual(person.rawObject!.value(forKey: "displayName") as! String?, "Mr. Joe")
|
||||
|
||||
person.rawObject!.setValue("AAAA", forKey: "displayName")
|
||||
XCTAssertEqual(person.rawObject!.value(forKey: "displayName") as! String?, "AAAA")
|
||||
|
||||
person.name .= "John"
|
||||
XCTAssertEqual(person.name.value, "Mr. John") // Custom getter
|
||||
XCTAssertEqual(person.name.value, "John")
|
||||
XCTAssertEqual(person.displayName.value, "Mr. John") // Custom getter
|
||||
|
||||
person.title .= "Sir"
|
||||
XCTAssertEqual(person.name.value, "Sir John")
|
||||
XCTAssertEqual(person.displayName.value, "Sir John")
|
||||
|
||||
person.pets.value.insert(dog)
|
||||
XCTAssertEqual(person.pets.count, 1)
|
||||
|
||||
@@ -256,6 +256,21 @@ final class WhereTests: XCTestCase {
|
||||
XCTAssertEqual(andWhere.predicate, andPredicate)
|
||||
XCTAssertEqual(andWhere, whereClause1 && whereClause2 && whereClause3)
|
||||
}
|
||||
do {
|
||||
|
||||
let andWhere = whereClause1 && whereClause2 && whereClause3
|
||||
let noneWhere: Where? = nil
|
||||
let someWhere: Where? = Where("key4", isEqualTo: "value4")
|
||||
|
||||
|
||||
let finalNoneWhere = andWhere && noneWhere
|
||||
let finalSomeWhere = andWhere && someWhere
|
||||
let unwrappedFinalSomeWhere = andWhere && someWhere!
|
||||
|
||||
|
||||
XCTAssertEqual(andWhere.predicate, finalNoneWhere.predicate)
|
||||
XCTAssertEqual(finalSomeWhere.predicate, unwrappedFinalSomeWhere.predicate)
|
||||
}
|
||||
do {
|
||||
|
||||
let orWhere = whereClause1 || whereClause2 || whereClause3
|
||||
@@ -272,6 +287,21 @@ final class WhereTests: XCTestCase {
|
||||
XCTAssertEqual(orWhere.predicate, orPredicate)
|
||||
XCTAssertEqual(orWhere, whereClause1 || whereClause2 || whereClause3)
|
||||
}
|
||||
do {
|
||||
|
||||
let orWhere = whereClause1 || whereClause2 || whereClause3
|
||||
let noneWhere: Where? = nil
|
||||
let someWhere: Where? = Where("key4", isEqualTo: "value4")
|
||||
|
||||
|
||||
let finalNoneWhere = orWhere && noneWhere
|
||||
let finalSomeWhere = orWhere && someWhere
|
||||
let unwrappedFinalSomeWhere = orWhere && someWhere!
|
||||
|
||||
XCTAssertEqual(orWhere.predicate, finalNoneWhere.predicate)
|
||||
XCTAssertEqual(finalSomeWhere.predicate, unwrappedFinalSomeWhere.predicate)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@objc
|
||||
|
||||
52
README.md
52
README.md
@@ -19,6 +19,7 @@ Unleashing the real power of Core Data with the elegance and safety of Swift
|
||||
</p>
|
||||
|
||||
* **Swift 3.1:** iOS 8+ / macOS 10.10+ / watchOS 2.0+ / tvOS 9.0+
|
||||
* Beta support: [Swift 3.2](https://github.com/JohnEstropia/CoreStore/tree/prototype/Swift_3_2), [Swift 4.0](https://github.com/JohnEstropia/CoreStore/tree/prototype/Swift_4_0)
|
||||
|
||||
Upgrading from CoreStore 3.x to 4.x? Check out the [new features](#features) and make sure to read the [Migration guide](#upgrading-from-3xx-to-4xx).
|
||||
|
||||
@@ -460,6 +461,55 @@ CoreStore.defaultStack = DataStack(
|
||||
)
|
||||
```
|
||||
|
||||
**`CoreStoreSchema`-based model versions with progressive migration**
|
||||
```swift
|
||||
typealias Animal = V2.Animal
|
||||
typealias Dog = V2.Dog
|
||||
typealias Person = V2.Person
|
||||
enum V2 {
|
||||
class Animal: CoreStoreObject {
|
||||
// ...
|
||||
}
|
||||
class Dog: Animal {
|
||||
// ...
|
||||
}
|
||||
class Person: CoreStoreObject {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
enum V1 {
|
||||
class Animal: CoreStoreObject {
|
||||
// ...
|
||||
}
|
||||
class Dog: Animal {
|
||||
// ...
|
||||
}
|
||||
class Person: CoreStoreObject {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
CoreStore.defaultStack = DataStack(
|
||||
CoreStoreSchema(
|
||||
modelVersion: "V1",
|
||||
entities: [
|
||||
Entity<V1.Animal>("Animal", isAbstract: true),
|
||||
Entity<V1.Dog>("Dog"),
|
||||
Entity<V1.Person>("Person")
|
||||
]
|
||||
),
|
||||
CoreStoreSchema(
|
||||
modelVersion: "V2",
|
||||
entities: [
|
||||
Entity<V2.Animal>("Animal", isAbstract: true),
|
||||
Entity<V2.Dog>("Dog"),
|
||||
Entity<V2.Person>("Person")
|
||||
]
|
||||
),
|
||||
migrationChain: ["V1", "V2"]
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
### Starting migrations
|
||||
We have seen `addStorageAndWait(...)` used to initialize our persistent store. As the method name's *~AndWait* suffix suggests though, this method blocks so it should not do long tasks such as store migrations. In fact CoreStore will only attempt a synchronous **lightweight** migration if you explicitly provide the `.allowSynchronousLightweightMigration` option:
|
||||
@@ -1672,7 +1722,7 @@ These special properties' values can be accessed or mutated using `.value`:
|
||||
```swift
|
||||
CoreStore.perform(
|
||||
asynchronous: { (transaction) in
|
||||
let dog: Dog = CoreStore.fetchOne(From<Dog>())!
|
||||
let dog: Dog = transaction.fetchOne(From<Dog>())!
|
||||
// ...
|
||||
let nickname = dog.nickname.value // String?
|
||||
let species = dog.species.value // String
|
||||
|
||||
@@ -210,6 +210,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
group.leave()
|
||||
}
|
||||
group.wait()
|
||||
self.context.reset()
|
||||
}
|
||||
|
||||
|
||||
|
||||
47
Sources/AttributeProtocol.swift
Normal file
47
Sources/AttributeProtocol.swift
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// AttributeProtocol.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 Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - AttributeProtocol
|
||||
|
||||
internal protocol AttributeProtocol: class {
|
||||
|
||||
static var attributeType: NSAttributeType { get }
|
||||
|
||||
var keyPath: KeyPath { get }
|
||||
var isOptional: Bool { get }
|
||||
var isIndexed: Bool { get }
|
||||
var isTransient: Bool { get }
|
||||
var versionHashModifier: () -> String? { get }
|
||||
var renamingIdentifier: () -> String? { get }
|
||||
var defaultValue: () -> Any? { get }
|
||||
var affectedByKeyPaths: () -> Set<String> { get }
|
||||
weak var parentObject: CoreStoreObject? { get set }
|
||||
var getter: CoreStoreManagedObject.CustomGetter? { get }
|
||||
var setter: CoreStoreManagedObject.CustomSetter? { get }
|
||||
}
|
||||
@@ -438,6 +438,11 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
return self.bypassesQueueing || self.transactionQueue.cs_isCurrentExecutionContext()
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
self.context.reset()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import CoreData
|
||||
- SeeAlso: `AsynchronousDataTransaction`
|
||||
*/
|
||||
@objc
|
||||
public final class CSAsynchronousDataTransaction: CSBaseDataTransaction {
|
||||
public final class CSAsynchronousDataTransaction: CSBaseDataTransaction, CoreStoreObjectiveCType {
|
||||
|
||||
/**
|
||||
Saves the transaction changes. This method should not be used after the `-commitWithCompletion:` method was already called once.
|
||||
@@ -139,9 +139,9 @@ public final class CSAsynchronousDataTransaction: CSBaseDataTransaction {
|
||||
|
||||
public typealias SwiftType = AsynchronousDataTransaction
|
||||
|
||||
public override var bridgeToSwift: AsynchronousDataTransaction {
|
||||
public var bridgeToSwift: AsynchronousDataTransaction {
|
||||
|
||||
return super.bridgeToSwift as! AsynchronousDataTransaction
|
||||
return super.swiftTransaction as! AsynchronousDataTransaction
|
||||
}
|
||||
|
||||
public required init(_ swiftValue: AsynchronousDataTransaction) {
|
||||
@@ -149,9 +149,9 @@ public final class CSAsynchronousDataTransaction: CSBaseDataTransaction {
|
||||
super.init(swiftValue as BaseDataTransaction)
|
||||
}
|
||||
|
||||
public required init(_ swiftValue: BaseDataTransaction) {
|
||||
public required override init(_ swiftValue: BaseDataTransaction) {
|
||||
|
||||
super.init(swiftValue as! AsynchronousDataTransaction)
|
||||
super.init(swiftValue)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public extension CSBaseDataTransaction {
|
||||
@objc
|
||||
public func fetchExistingObject(_ object: NSManagedObject) -> Any? {
|
||||
|
||||
return self.bridgeToSwift.context.fetchExisting(object) as NSManagedObject?
|
||||
return self.swiftTransaction.context.fetchExisting(object) as NSManagedObject?
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ public extension CSBaseDataTransaction {
|
||||
@objc
|
||||
public func fetchExistingObjectWithID(_ objectID: NSManagedObjectID) -> Any? {
|
||||
|
||||
return self.bridgeToSwift.context.fetchExisting(objectID) as NSManagedObject?
|
||||
return self.swiftTransaction.context.fetchExisting(objectID) as NSManagedObject?
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,7 +64,7 @@ public extension CSBaseDataTransaction {
|
||||
@objc
|
||||
public func fetchExistingObjects(_ objects: [NSManagedObject]) -> [Any] {
|
||||
|
||||
return self.bridgeToSwift.context.fetchExisting(objects) as [NSManagedObject]
|
||||
return self.swiftTransaction.context.fetchExisting(objects) as [NSManagedObject]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +76,7 @@ public extension CSBaseDataTransaction {
|
||||
@objc
|
||||
public func fetchExistingObjectsWithIDs(_ objectIDs: [NSManagedObjectID]) -> [Any] {
|
||||
|
||||
return self.bridgeToSwift.context.fetchExisting(objectIDs) as [NSManagedObject]
|
||||
return self.swiftTransaction.context.fetchExisting(objectIDs) as [NSManagedObject]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,10 +90,10 @@ public extension CSBaseDataTransaction {
|
||||
public func fetchOneFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> Any? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
||||
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
return self.bridgeToSwift.context.fetchOne(from, fetchClauses)
|
||||
return self.swiftTransaction.context.fetchOne(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,10 +107,10 @@ public extension CSBaseDataTransaction {
|
||||
public func fetchAllFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> [Any]? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
||||
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
return self.bridgeToSwift.context.fetchAll(from, fetchClauses)
|
||||
return self.swiftTransaction.context.fetchAll(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,10 +124,10 @@ public extension CSBaseDataTransaction {
|
||||
public func fetchCountFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> NSNumber? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
||||
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
return self.bridgeToSwift.context
|
||||
return self.swiftTransaction.context
|
||||
.fetchCount(from, fetchClauses)
|
||||
.flatMap { NSNumber(value: $0) }
|
||||
}
|
||||
@@ -143,10 +143,10 @@ public extension CSBaseDataTransaction {
|
||||
public func fetchObjectIDFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> NSManagedObjectID? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
||||
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
return self.bridgeToSwift.context.fetchObjectID(from, fetchClauses)
|
||||
return self.swiftTransaction.context.fetchObjectID(from, fetchClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,10 +163,10 @@ public extension CSBaseDataTransaction {
|
||||
public func queryValueFrom(_ from: CSFrom, selectClause: CSSelect, queryClauses: [CSQueryClause]) -> Any? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
||||
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
return self.bridgeToSwift.context.queryValue(from, selectClause, queryClauses)
|
||||
return self.swiftTransaction.context.queryValue(from, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,9 +183,9 @@ public extension CSBaseDataTransaction {
|
||||
public func queryAttributesFrom(_ from: CSFrom, selectClause: CSSelect, queryClauses: [CSQueryClause]) -> [[String: Any]]? {
|
||||
|
||||
CoreStore.assert(
|
||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
||||
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
|
||||
)
|
||||
return self.bridgeToSwift.context.queryAttributes(from, selectClause, queryClauses)
|
||||
return self.swiftTransaction.context.queryAttributes(from, selectClause, queryClauses)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import CoreData
|
||||
- SeeAlso: `BaseDataTransaction`
|
||||
*/
|
||||
@objc
|
||||
public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
public class CSBaseDataTransaction: NSObject {
|
||||
|
||||
// MARK: Object management
|
||||
|
||||
@@ -45,7 +45,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public var hasChanges: Bool {
|
||||
|
||||
return self.bridgeToSwift.hasChanges
|
||||
return self.swiftTransaction.hasChanges
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func createInto(_ into: CSInto) -> Any {
|
||||
|
||||
return self.bridgeToSwift.create(into.bridgeToSwift)
|
||||
return self.swiftTransaction.create(into.bridgeToSwift)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func editObject(_ object: NSManagedObject?) -> Any? {
|
||||
|
||||
return self.bridgeToSwift.edit(object)
|
||||
return self.swiftTransaction.edit(object)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +82,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func editInto(_ into: CSInto, objectID: NSManagedObjectID) -> Any? {
|
||||
|
||||
return self.bridgeToSwift.edit(into.bridgeToSwift, objectID)
|
||||
return self.swiftTransaction.edit(into.bridgeToSwift, objectID)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +93,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func deleteObject(_ object: NSManagedObject?) {
|
||||
|
||||
self.bridgeToSwift.delete(object)
|
||||
self.swiftTransaction.delete(object)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +104,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func deleteObjects(_ objects: [NSManagedObject]) {
|
||||
|
||||
self.bridgeToSwift.delete(objects)
|
||||
self.swiftTransaction.delete(objects)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,7 +113,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func refreshAndMergeAllObjects() {
|
||||
|
||||
self.bridgeToSwift.refreshAndMergeAllObjects()
|
||||
self.swiftTransaction.refreshAndMergeAllObjects()
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func insertedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> {
|
||||
|
||||
return self.bridgeToSwift.insertedObjects(entity)
|
||||
return self.swiftTransaction.insertedObjects(entity)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +139,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func insertedObjectIDs() -> Set<NSManagedObjectID> {
|
||||
|
||||
return self.bridgeToSwift.insertedObjectIDs()
|
||||
return self.swiftTransaction.insertedObjectIDs()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +151,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func insertedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> {
|
||||
|
||||
return self.bridgeToSwift.insertedObjectIDs(entity)
|
||||
return self.swiftTransaction.insertedObjectIDs(entity)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,7 +163,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func updatedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> {
|
||||
|
||||
return self.bridgeToSwift.updatedObjects(entity)
|
||||
return self.swiftTransaction.updatedObjects(entity)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,7 +174,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func updatedObjectIDs() -> Set<NSManagedObjectID> {
|
||||
|
||||
return self.bridgeToSwift.updatedObjectIDs()
|
||||
return self.swiftTransaction.updatedObjectIDs()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +186,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func updatedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> {
|
||||
|
||||
return self.bridgeToSwift.updatedObjectIDs(entity)
|
||||
return self.swiftTransaction.updatedObjectIDs(entity)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,7 +198,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func deletedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> {
|
||||
|
||||
return self.bridgeToSwift.deletedObjects(entity)
|
||||
return self.swiftTransaction.deletedObjects(entity)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +209,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func deletedObjectIDs() -> Set<NSManagedObjectID> {
|
||||
|
||||
return self.bridgeToSwift.deletedObjectIDs()
|
||||
return self.swiftTransaction.deletedObjectIDs()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,7 +221,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func deletedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> {
|
||||
|
||||
return self.bridgeToSwift.deletedObjectIDs(entity)
|
||||
return self.swiftTransaction.deletedObjectIDs(entity)
|
||||
}
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
public override var hash: Int {
|
||||
|
||||
return ObjectIdentifier(self.bridgeToSwift).hashValue
|
||||
return ObjectIdentifier(self.swiftTransaction).hashValue
|
||||
}
|
||||
|
||||
public override func isEqual(_ object: Any?) -> Bool {
|
||||
@@ -238,28 +238,20 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
|
||||
return false
|
||||
}
|
||||
return self.bridgeToSwift === object.bridgeToSwift
|
||||
return self.swiftTransaction === object.swiftTransaction
|
||||
}
|
||||
|
||||
|
||||
// MARK: CoreStoreObjectiveCType
|
||||
// MARK: Internal
|
||||
|
||||
public required init(_ swiftValue: BaseDataTransaction) {
|
||||
internal let swiftTransaction: BaseDataTransaction
|
||||
|
||||
internal init(_ swiftValue: BaseDataTransaction) {
|
||||
|
||||
self.swiftTransaction = swiftValue
|
||||
super.init()
|
||||
}
|
||||
|
||||
public var bridgeToSwift: BaseDataTransaction {
|
||||
|
||||
return self.swiftTransaction
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let swiftTransaction: BaseDataTransaction
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@@ -267,20 +259,20 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
||||
@objc
|
||||
public func insertedObjects() -> Set<NSManagedObject> {
|
||||
|
||||
return self.bridgeToSwift.insertedObjects()
|
||||
return self.swiftTransaction.insertedObjects()
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Use -[updatedObjectsOfType:] and pass the specific entity class")
|
||||
@objc
|
||||
public func updatedObjects() -> Set<NSManagedObject> {
|
||||
|
||||
return self.bridgeToSwift.updatedObjects()
|
||||
return self.swiftTransaction.updatedObjects()
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Use -[deletedObjectsOfType:] and pass the specific entity class")
|
||||
@objc
|
||||
public func deletedObjects() -> Set<NSManagedObject> {
|
||||
|
||||
return self.bridgeToSwift.deletedObjects()
|
||||
return self.swiftTransaction.deletedObjects()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import CoreData
|
||||
- SeeAlso: `SynchronousDataTransaction`
|
||||
*/
|
||||
@objc
|
||||
public final class CSSynchronousDataTransaction: CSBaseDataTransaction {
|
||||
public final class CSSynchronousDataTransaction: CSBaseDataTransaction, CoreStoreObjectiveCType {
|
||||
|
||||
/**
|
||||
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `-commitAndWaitWithError:` method was already called once.
|
||||
@@ -129,9 +129,9 @@ public final class CSSynchronousDataTransaction: CSBaseDataTransaction {
|
||||
|
||||
public typealias SwiftType = SynchronousDataTransaction
|
||||
|
||||
public override var bridgeToSwift: SynchronousDataTransaction {
|
||||
public var bridgeToSwift: SynchronousDataTransaction {
|
||||
|
||||
return super.bridgeToSwift as! SynchronousDataTransaction
|
||||
return super.swiftTransaction as! SynchronousDataTransaction
|
||||
}
|
||||
|
||||
public required init(_ swiftValue: SynchronousDataTransaction) {
|
||||
@@ -139,9 +139,9 @@ public final class CSSynchronousDataTransaction: CSBaseDataTransaction {
|
||||
super.init(swiftValue as BaseDataTransaction)
|
||||
}
|
||||
|
||||
public required init(_ swiftValue: BaseDataTransaction) {
|
||||
public required override init(_ swiftValue: BaseDataTransaction) {
|
||||
|
||||
super.init(swiftValue as! SynchronousDataTransaction)
|
||||
super.init(swiftValue)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import CoreData
|
||||
- SeeAlso: `UnsafeDataTransaction`
|
||||
*/
|
||||
@objc
|
||||
public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
|
||||
public final class CSUnsafeDataTransaction: CSBaseDataTransaction, CoreStoreObjectiveCType {
|
||||
/**
|
||||
Saves the transaction changes asynchronously. For a `CSUnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
|
||||
|
||||
@@ -189,9 +189,9 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
|
||||
|
||||
public typealias SwiftType = UnsafeDataTransaction
|
||||
|
||||
public override var bridgeToSwift: UnsafeDataTransaction {
|
||||
public var bridgeToSwift: UnsafeDataTransaction {
|
||||
|
||||
return super.bridgeToSwift as! UnsafeDataTransaction
|
||||
return super.swiftTransaction as! UnsafeDataTransaction
|
||||
}
|
||||
|
||||
public required init(_ swiftValue: UnsafeDataTransaction) {
|
||||
@@ -199,9 +199,9 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
|
||||
super.init(swiftValue as BaseDataTransaction)
|
||||
}
|
||||
|
||||
public required init(_ swiftValue: BaseDataTransaction) {
|
||||
public required override init(_ swiftValue: BaseDataTransaction) {
|
||||
|
||||
super.init(swiftValue as! UnsafeDataTransaction)
|
||||
super.init(swiftValue)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -868,7 +868,8 @@ extension SQLiteStore: CustomDebugStringConvertible, CoreStoreDebugStringConvert
|
||||
("storeOptions", self.storeOptions as Any),
|
||||
("fileURL", self.fileURL),
|
||||
("migrationMappingProviders", self.migrationMappingProviders),
|
||||
("localStorageOptions", self.localStorageOptions)
|
||||
("localStorageOptions", self.localStorageOptions),
|
||||
("fileSize", self.fileSize() as Any)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public extension CoreStore {
|
||||
}
|
||||
|
||||
/**
|
||||
Using the `defaultStack`, performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be rethrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
Using the `defaultStack`, performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be thrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- parameter task: the synchronous non-escaping closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- parameter waitForAllObservers: When `true`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks. When `false`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning. Defaults to `true`.
|
||||
|
||||
@@ -235,15 +235,16 @@ CSWhere *_Nonnull CSWherePredicate(NSPredicate *_Nonnull predicate) CORESTORE_RE
|
||||
|
||||
- (void)setAffectedStores:(NSArray<NSPersistentStore *> *_Nullable)affectedStores {
|
||||
|
||||
// Bugfix for NSFetchRequest messing up memory management for `affectedStores`
|
||||
// http://stackoverflow.com/questions/14396375/nsfetchedresultscontroller-crashes-in-ios-6-if-affectedstores-is-specified
|
||||
|
||||
if (NSFoundationVersionNumber < NSFoundationVersionNumber10_0) {
|
||||
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
|
||||
if ([processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){ 11, 0, 0 }]
|
||||
|| ![processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){ 10, 0, 0 }]) {
|
||||
|
||||
self.safeAffectedStores = affectedStores;
|
||||
[super setAffectedStores:affectedStores];
|
||||
return;
|
||||
}
|
||||
// Bugfix for NSFetchRequest messing up memory management for `affectedStores`
|
||||
// http://stackoverflow.com/questions/14396375/nsfetchedresultscontroller-crashes-in-ios-6-if-affectedstores-is-specified
|
||||
if (self.releaseArray != NULL) {
|
||||
|
||||
CFRelease(self.releaseArray);
|
||||
|
||||
@@ -35,7 +35,7 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll
|
||||
// MARK: Internal
|
||||
|
||||
@nonobjc
|
||||
internal convenience init<T: DynamicObject>(dataStack: DataStack, fetchRequest: NSFetchRequest<NSManagedObject>, from: From<T>?, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest<NSManagedObject>) -> Void) {
|
||||
internal convenience init<T: DynamicObject>(dataStack: DataStack, fetchRequest: NSFetchRequest<NSManagedObject>, from: From<T>, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest<NSManagedObject>) -> Void) {
|
||||
|
||||
self.init(
|
||||
context: dataStack.mainContext,
|
||||
@@ -47,33 +47,18 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal init<T: DynamicObject>(context: NSManagedObjectContext, fetchRequest: NSFetchRequest<NSManagedObject>, from: From<T>?, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest<NSManagedObject>) -> Void) {
|
||||
internal init<T: DynamicObject>(context: NSManagedObjectContext, fetchRequest: NSFetchRequest<NSManagedObject>, from: From<T>, sectionBy: SectionBy? = nil, applyFetchClauses: @escaping (_ fetchRequest: NSFetchRequest<NSManagedObject>) -> Void) {
|
||||
|
||||
_ = from?.applyToFetchRequest(
|
||||
_ = from.applyToFetchRequest(
|
||||
fetchRequest,
|
||||
context: context,
|
||||
applyAffectedStores: false
|
||||
)
|
||||
applyFetchClauses(fetchRequest)
|
||||
|
||||
if let from = from {
|
||||
self.reapplyAffectedStores = { fetchRequest, context in
|
||||
|
||||
self.reapplyAffectedStores = { fetchRequest, context in
|
||||
|
||||
return from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
guard let from = (fetchRequest.entity.flatMap { $0.managedObjectClassName }).flatMap(NSClassFromString).flatMap({ From<T>($0 as! T.Type) }) else {
|
||||
|
||||
CoreStore.abort("Attempted to create a \(CoreStoreFetchedResultsController.self) without a \(cs_typeName(From<T>.self)) clause or an \(cs_typeName(NSEntityDescription.self)).")
|
||||
}
|
||||
|
||||
self.reapplyAffectedStores = { fetchRequest, context in
|
||||
|
||||
return from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
|
||||
}
|
||||
return from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
|
||||
}
|
||||
|
||||
super.init(
|
||||
|
||||
34
Sources/CoreStoreManagedObject.swift
Normal file
34
Sources/CoreStoreManagedObject.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// CoreStoreManagedObject.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Created by John Rommel Estropia on 2017/06/04.
|
||||
// Copyright © 2017 John Rommel Estropia. All rights reserved.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - CoreStoreManagedObject
|
||||
|
||||
@objc internal class CoreStoreManagedObject: NSManagedObject {
|
||||
|
||||
internal typealias CustomGetter = @convention(block) (_ rawObject: Any) -> Any?
|
||||
internal typealias CustomSetter = @convention(block) (_ rawObject: Any, _ newValue: Any?) -> Void
|
||||
internal typealias CustomGetterSetter = (getter: CustomGetter?, setter: CustomSetter?)
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
internal static func cs_subclassName(for entity: DynamicEntity, in modelVersion: ModelVersion) -> String {
|
||||
|
||||
return "_\(NSStringFromClass(CoreStoreManagedObject.self))__\(modelVersion)__\(NSStringFromClass(entity.type))__\(entity.entityName)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private enum Static {
|
||||
|
||||
static let queue = DispatchQueue.concurrent("com.coreStore.coreStoreManagerObjectBarrierQueue")
|
||||
static var cache: [ObjectIdentifier: [KeyPath: Set<KeyPath>]] = [:]
|
||||
}
|
||||
@@ -53,6 +53,39 @@ public extension DynamicObject where Self: CoreStoreObject {
|
||||
return attribute(self.meta).keyPath
|
||||
}
|
||||
|
||||
/**
|
||||
Extracts the keyPath string from a `CoreStoreObject.Relationship` property.
|
||||
```
|
||||
let keyPath: String = Person.keyPath { $0.pets }
|
||||
```
|
||||
*/
|
||||
public static func keyPath<O: CoreStoreObject, D: CoreStoreObject>(_ relationship: (Self) -> RelationshipContainer<O>.ToOne<D>) -> String {
|
||||
|
||||
return relationship(self.meta).keyPath
|
||||
}
|
||||
|
||||
/**
|
||||
Extracts the keyPath string from a `CoreStoreObject.Relationship` property.
|
||||
```
|
||||
let keyPath: String = Person.keyPath { $0.pets }
|
||||
```
|
||||
*/
|
||||
public static func keyPath<O: CoreStoreObject, D: CoreStoreObject>(_ relationship: (Self) -> RelationshipContainer<O>.ToManyOrdered<D>) -> String {
|
||||
|
||||
return relationship(self.meta).keyPath
|
||||
}
|
||||
|
||||
/**
|
||||
Extracts the keyPath string from a `CoreStoreObject.Relationship` property.
|
||||
```
|
||||
let keyPath: String = Person.keyPath { $0.pets }
|
||||
```
|
||||
*/
|
||||
public static func keyPath<O: CoreStoreObject, D: CoreStoreObject>(_ relationship: (Self) -> RelationshipContainer<O>.ToManyUnordered<D>) -> String {
|
||||
|
||||
return relationship(self.meta).keyPath
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
@@ -67,10 +100,10 @@ public extension DynamicObject where Self: CoreStoreObject {
|
||||
/**
|
||||
Creates an `OrderBy` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let person = CoreStore.fetchAll(From<Person>(), Person.ascending { $0.age })
|
||||
let person = CoreStore.fetchAll(From<Person>(), Person.orderBy(ascending: { $0.age }))
|
||||
```
|
||||
*/
|
||||
public static func ascending<O: CoreStoreObject, V: ImportableAttributeType>(_ attribute: (Self) -> ValueContainer<O>.Optional<V>) -> OrderBy {
|
||||
public static func orderBy<O: CoreStoreObject, V: ImportableAttributeType>(ascending attribute: (Self) -> ValueContainer<O>.Required<V>) -> OrderBy {
|
||||
|
||||
return OrderBy(.ascending(attribute(self.meta).keyPath))
|
||||
}
|
||||
@@ -78,9 +111,46 @@ public extension DynamicObject where Self: CoreStoreObject {
|
||||
/**
|
||||
Creates an `OrderBy` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let person = CoreStore.fetchAll(From<Person>(), Person.descending { $0.age })
|
||||
let person = CoreStore.fetchAll(From<Person>(), Person.orderBy(ascending: { $0.age }))
|
||||
```
|
||||
*/
|
||||
public static func orderBy<O: CoreStoreObject, V: ImportableAttributeType>(ascending attribute: (Self) -> ValueContainer<O>.Optional<V>) -> OrderBy {
|
||||
|
||||
return OrderBy(.ascending(attribute(self.meta).keyPath))
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `OrderBy` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let person = CoreStore.fetchAll(From<Person>(), Person.orderBy(descending: { $0.age }))
|
||||
```
|
||||
*/
|
||||
public static func orderBy<O: CoreStoreObject, V: ImportableAttributeType>(descending attribute: (Self) -> ValueContainer<O>.Required<V>) -> OrderBy {
|
||||
|
||||
return OrderBy(.descending(attribute(self.meta).keyPath))
|
||||
}
|
||||
|
||||
/**
|
||||
Creates an `OrderBy` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let person = CoreStore.fetchAll(From<Person>(), Person.orderBy(descending: { $0.age }))
|
||||
```
|
||||
*/
|
||||
public static func orderBy<O: CoreStoreObject, V: ImportableAttributeType>(descending attribute: (Self) -> ValueContainer<O>.Optional<V>) -> OrderBy {
|
||||
|
||||
return OrderBy(.descending(attribute(self.meta).keyPath))
|
||||
}
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated, renamed: "orderBy(ascending:)")
|
||||
public static func ascending<O: CoreStoreObject, V: ImportableAttributeType>(_ attribute: (Self) -> ValueContainer<O>.Optional<V>) -> OrderBy {
|
||||
|
||||
return OrderBy(.ascending(attribute(self.meta).keyPath))
|
||||
}
|
||||
|
||||
@available(*, deprecated, renamed: "orderBy(descending:)")
|
||||
public static func descending<O: CoreStoreObject, V: ImportableAttributeType>(_ attribute: (Self) -> ValueContainer<O>.Optional<V>) -> OrderBy {
|
||||
|
||||
return OrderBy(.descending(attribute(self.meta).keyPath))
|
||||
@@ -163,6 +233,18 @@ public extension ValueContainer.Required {
|
||||
|
||||
return Where("%K >= %@", attribute.keyPath, value)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>(), Dog.where { ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func ~= <S: Sequence>(_ sequence: S, _ attribute: ValueContainer<O>.Required<V>) -> Where where S.Iterator.Element == V {
|
||||
|
||||
return Where(attribute.keyPath, isMemberOf: sequence)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -193,4 +275,146 @@ public extension ValueContainer.Optional {
|
||||
|
||||
return !Where(attribute.keyPath, isEqualTo: value)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>(), Person.where { $0.age < 20 })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func < (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where {
|
||||
|
||||
if let value = value {
|
||||
|
||||
return Where("%K < %@", attribute.keyPath, value)
|
||||
}
|
||||
else {
|
||||
|
||||
return Where("%K < nil", attribute.keyPath)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>(), Person.where { $0.age > 20 })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func > (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where {
|
||||
|
||||
if let value = value {
|
||||
|
||||
return Where("%K > %@", attribute.keyPath, value)
|
||||
}
|
||||
else {
|
||||
|
||||
return Where("%K > nil", attribute.keyPath)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>(), Person.where { $0.age <= 20 })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func <= (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where {
|
||||
|
||||
if let value = value {
|
||||
|
||||
return Where("%K <= %@", attribute.keyPath, value)
|
||||
}
|
||||
else {
|
||||
|
||||
return Where("%K <= nil", attribute.keyPath)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let person = CoreStore.fetchOne(From<Person>(), Person.where { $0.age >= 20 })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func >= (_ attribute: ValueContainer<O>.Optional<V>, _ value: V?) -> Where {
|
||||
|
||||
if let value = value {
|
||||
|
||||
return Where("%K >= %@", attribute.keyPath, value)
|
||||
}
|
||||
else {
|
||||
|
||||
return Where("%K >= nil", attribute.keyPath)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Value` property.
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>(), Dog.where { ["Pluto", "Snoopy", "Scooby"] ~= $0.nickname })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func ~= <S: Sequence>(_ sequence: S, _ attribute: ValueContainer<O>.Optional<V>) -> Where where S.Iterator.Element == V {
|
||||
|
||||
return Where(attribute.keyPath, isMemberOf: sequence)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - RelationshipContainer.ToOne
|
||||
|
||||
public extension RelationshipContainer.ToOne {
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Relationship` property.
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>(), Dog.where { $0.master == me })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func == (_ relationship: RelationshipContainer<O>.ToOne<D>, _ object: D?) -> Where {
|
||||
|
||||
return Where(relationship.keyPath, isEqualTo: object)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Relationship` property.
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>(), Dog.where { $0.master != me })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func != (_ relationship: RelationshipContainer<O>.ToOne<D>, _ object: D?) -> Where {
|
||||
|
||||
return !Where(relationship.keyPath, isEqualTo: object)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Relationship` property.
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>(), Dog.where { $0.master ~= me })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func ~= (_ relationship: RelationshipContainer<O>.ToOne<D>, _ object: D?) -> Where {
|
||||
|
||||
return Where(relationship.keyPath, isEqualTo: object)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause from a `CoreStoreObject.Relationship` property.
|
||||
```
|
||||
let dog = CoreStore.fetchOne(From<Dog>(), Dog.where { [john, joe, bob] ~= $0.master })
|
||||
```
|
||||
*/
|
||||
@inline(__always)
|
||||
public static func ~= <S: Sequence>(_ sequence: S, _ relationship: RelationshipContainer<O>.ToOne<D>) -> Where where S.Iterator.Element == D {
|
||||
|
||||
return Where(relationship.keyPath, isMemberOf: sequence)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
||||
public required init(rawObject: NSManagedObject) {
|
||||
|
||||
self.isMeta = false
|
||||
self.rawObject = rawObject
|
||||
self.initializeAttributes(Mirror(reflecting: self), { [unowned self] in self })
|
||||
self.rawObject = (rawObject as! CoreStoreManagedObject)
|
||||
self.initializeAttributes(Mirror(reflecting: self), self)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,13 +110,13 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal let rawObject: NSManagedObject?
|
||||
internal let rawObject: CoreStoreManagedObject?
|
||||
internal let isMeta: Bool
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func initializeAttributes(_ mirror: Mirror, _ parentObject: @escaping () -> CoreStoreObject) {
|
||||
private func initializeAttributes(_ mirror: Mirror, _ parentObject: CoreStoreObject) {
|
||||
|
||||
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, parentObject) })
|
||||
for child in mirror.children {
|
||||
@@ -135,3 +135,23 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - DynamicObject where Self: CoreStoreObject
|
||||
|
||||
public extension DynamicObject where Self: CoreStoreObject {
|
||||
|
||||
public func partialObject() -> PartialObject<Self> {
|
||||
|
||||
CoreStore.assert(
|
||||
!self.isMeta,
|
||||
"Attempted to create a \(cs_typeName(PartialObject<Self>.self)) from a meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return PartialObject<Self>(self.rawObject!)
|
||||
}
|
||||
|
||||
internal static var meta: Self {
|
||||
|
||||
return self.init(asMeta: ())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,35 +200,44 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
|
||||
public func rawModel() -> NSManagedObjectModel {
|
||||
|
||||
if let cachedRawModel = self.cachedRawModel {
|
||||
return CoreStoreSchema.barrierQueue.sync(flags: .barrier) {
|
||||
|
||||
return cachedRawModel
|
||||
}
|
||||
let rawModel = NSManagedObjectModel()
|
||||
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||
for entity in self.allEntities {
|
||||
|
||||
let entityDescription = self.entityDescription(
|
||||
for: entity,
|
||||
initializer: CoreStoreSchema.firstPassCreateEntityDescription
|
||||
if let cachedRawModel = self.cachedRawModel {
|
||||
|
||||
return cachedRawModel
|
||||
}
|
||||
let rawModel = NSManagedObjectModel()
|
||||
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||
var allCustomGettersSetters: [DynamicEntity: [KeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||
for entity in self.allEntities {
|
||||
|
||||
let (entityDescription, customGetterSetterByKeyPaths) = self.entityDescription(
|
||||
for: entity,
|
||||
initializer: CoreStoreSchema.firstPassCreateEntityDescription(from:in:)
|
||||
)
|
||||
entityDescriptionsByEntity[entity] = (entityDescription.copy() as! NSEntityDescription)
|
||||
allCustomGettersSetters[entity] = customGetterSetterByKeyPaths
|
||||
}
|
||||
CoreStoreSchema.secondPassConnectRelationshipAttributes(for: entityDescriptionsByEntity)
|
||||
CoreStoreSchema.thirdPassConnectInheritanceTree(for: entityDescriptionsByEntity)
|
||||
CoreStoreSchema.fourthPassSynthesizeManagedObjectClasses(
|
||||
for: entityDescriptionsByEntity,
|
||||
allCustomGettersSetters: allCustomGettersSetters
|
||||
)
|
||||
entityDescriptionsByEntity[entity] = (entityDescription.copy() as! NSEntityDescription)
|
||||
}
|
||||
CoreStoreSchema.secondPassConnectRelationshipAttributes(for: entityDescriptionsByEntity)
|
||||
CoreStoreSchema.thirdPassConnectInheritanceTree(for: entityDescriptionsByEntity)
|
||||
|
||||
rawModel.entities = entityDescriptionsByEntity.values.sorted(by: { $0.name! < $1.name! })
|
||||
for (configuration, entities) in self.entitiesByConfiguration {
|
||||
|
||||
rawModel.setEntities(
|
||||
entities
|
||||
.map({ entityDescriptionsByEntity[$0]! })
|
||||
.sorted(by: { $0.name! < $1.name! }),
|
||||
forConfigurationName: configuration
|
||||
)
|
||||
rawModel.entities = entityDescriptionsByEntity.values.sorted(by: { $0.name! < $1.name! })
|
||||
for (configuration, entities) in self.entitiesByConfiguration {
|
||||
|
||||
rawModel.setEntities(
|
||||
entities
|
||||
.map({ entityDescriptionsByEntity[$0]! })
|
||||
.sorted(by: { $0.name! < $1.name! }),
|
||||
forConfigurationName: configuration
|
||||
)
|
||||
}
|
||||
self.cachedRawModel = rawModel
|
||||
return rawModel
|
||||
}
|
||||
self.cachedRawModel = rawModel
|
||||
return rawModel
|
||||
}
|
||||
|
||||
|
||||
@@ -244,28 +253,33 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
private let allEntities: Set<DynamicEntity>
|
||||
|
||||
private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||
private var customGettersSettersByEntity: [DynamicEntity: [KeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||
private weak var cachedRawModel: NSManagedObjectModel?
|
||||
|
||||
private func entityDescription(for entity: DynamicEntity, initializer: (DynamicEntity) -> NSEntityDescription) -> NSEntityDescription {
|
||||
private func entityDescription(for entity: DynamicEntity, initializer: (DynamicEntity, ModelVersion) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [KeyPath: CoreStoreManagedObject.CustomGetterSetter])) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [KeyPath: CoreStoreManagedObject.CustomGetterSetter]) {
|
||||
|
||||
if let cachedEntityDescription = self.entityDescriptionsByEntity[entity] {
|
||||
|
||||
return cachedEntityDescription
|
||||
return (cachedEntityDescription, self.customGettersSettersByEntity[entity] ?? [:])
|
||||
}
|
||||
let entityDescription = withoutActuallyEscaping(initializer, do: { $0(entity) })
|
||||
let modelVersion = self.modelVersion
|
||||
let (entityDescription, customGetterSetterByKeyPaths) = withoutActuallyEscaping(initializer, do: { $0(entity, modelVersion) })
|
||||
self.entityDescriptionsByEntity[entity] = entityDescription
|
||||
return entityDescription
|
||||
self.customGettersSettersByEntity[entity] = customGetterSetterByKeyPaths
|
||||
return (entityDescription, customGetterSetterByKeyPaths)
|
||||
}
|
||||
|
||||
private static func firstPassCreateEntityDescription(from entity: DynamicEntity) -> NSEntityDescription {
|
||||
private static func firstPassCreateEntityDescription(from entity: DynamicEntity, in modelVersion: ModelVersion) -> (entity: NSEntityDescription, customGetterSetterByKeyPaths: [KeyPath: CoreStoreManagedObject.CustomGetterSetter]) {
|
||||
|
||||
let entityDescription = NSEntityDescription()
|
||||
entityDescription.coreStoreEntity = entity
|
||||
entityDescription.name = entity.entityName
|
||||
entityDescription.isAbstract = entity.isAbstract
|
||||
entityDescription.versionHashModifier = entity.versionHashModifier
|
||||
entityDescription.managedObjectClassName = NSStringFromClass(NSManagedObject.self)
|
||||
entityDescription.managedObjectClassName = CoreStoreManagedObject.cs_subclassName(for: entity, in: modelVersion)
|
||||
|
||||
var keyPathsByAffectedKeyPaths: [KeyPath: Set<KeyPath>] = [:]
|
||||
var customGetterSetterByKeyPaths: [KeyPath: CoreStoreManagedObject.CustomGetterSetter] = [:]
|
||||
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
|
||||
|
||||
var propertyDescriptions: [NSPropertyDescription] = []
|
||||
@@ -279,11 +293,13 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
description.attributeType = type(of: attribute).attributeType
|
||||
description.isOptional = attribute.isOptional
|
||||
description.isIndexed = attribute.isIndexed
|
||||
description.defaultValue = attribute.defaultValue
|
||||
description.defaultValue = attribute.defaultValue()
|
||||
description.isTransient = attribute.isTransient
|
||||
description.versionHashModifier = attribute.versionHashModifier
|
||||
description.renamingIdentifier = attribute.renamingIdentifier
|
||||
description.versionHashModifier = attribute.versionHashModifier()
|
||||
description.renamingIdentifier = attribute.renamingIdentifier()
|
||||
propertyDescriptions.append(description)
|
||||
keyPathsByAffectedKeyPaths[attribute.keyPath] = attribute.affectedByKeyPaths()
|
||||
customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter)
|
||||
|
||||
case let relationship as RelationshipProtocol:
|
||||
let description = NSRelationshipDescription()
|
||||
@@ -292,9 +308,10 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
description.maxCount = relationship.maxCount
|
||||
description.isOrdered = relationship.isOrdered
|
||||
description.deleteRule = relationship.deleteRule
|
||||
description.versionHashModifier = relationship.versionHashModifier
|
||||
description.renamingIdentifier = relationship.renamingIdentifier
|
||||
description.versionHashModifier = relationship.versionHashModifier()
|
||||
description.renamingIdentifier = relationship.renamingIdentifier()
|
||||
propertyDescriptions.append(description)
|
||||
keyPathsByAffectedKeyPaths[relationship.keyPath] = relationship.affectedByKeyPaths()
|
||||
|
||||
default:
|
||||
continue
|
||||
@@ -302,9 +319,9 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
}
|
||||
return propertyDescriptions
|
||||
}
|
||||
|
||||
entityDescription.properties = createProperties(for: entity.type as! CoreStoreObject.Type)
|
||||
return entityDescription
|
||||
entityDescription.keyPathsByAffectedKeyPaths = keyPathsByAffectedKeyPaths
|
||||
return (entityDescription, customGetterSetterByKeyPaths)
|
||||
}
|
||||
|
||||
private static func secondPassConnectRelationshipAttributes(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription]) {
|
||||
@@ -425,4 +442,112 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private static func fourthPassSynthesizeManagedObjectClasses(for entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription], allCustomGettersSetters: [DynamicEntity: [KeyPath: CoreStoreManagedObject.CustomGetterSetter]]) {
|
||||
|
||||
func createManagedObjectSubclass(for entityDescription: NSEntityDescription, customGetterSetterByKeyPaths: [KeyPath: CoreStoreManagedObject.CustomGetterSetter]?) {
|
||||
|
||||
let superEntity = entityDescription.superentity
|
||||
let className = entityDescription.managedObjectClassName!
|
||||
guard case nil = NSClassFromString(className) as! CoreStoreManagedObject.Type? else {
|
||||
|
||||
return
|
||||
}
|
||||
if let superEntity = superEntity {
|
||||
|
||||
createManagedObjectSubclass(
|
||||
for: superEntity,
|
||||
customGetterSetterByKeyPaths: superEntity.coreStoreEntity.flatMap({ allCustomGettersSetters[$0] })
|
||||
)
|
||||
}
|
||||
let superClass = cs_lazy { () -> CoreStoreManagedObject.Type in
|
||||
|
||||
if let superClassName = superEntity?.managedObjectClassName,
|
||||
let superClass = NSClassFromString(superClassName) {
|
||||
|
||||
return superClass as! CoreStoreManagedObject.Type
|
||||
}
|
||||
return CoreStoreManagedObject.self
|
||||
}
|
||||
let managedObjectClass: AnyClass = className.withCString {
|
||||
|
||||
return objc_allocateClassPair(superClass, $0, 0)!
|
||||
}
|
||||
defer {
|
||||
|
||||
objc_registerClassPair(managedObjectClass)
|
||||
}
|
||||
|
||||
func capitalize(_ string: String) -> String {
|
||||
|
||||
return string.replacingCharacters(
|
||||
in: Range(uncheckedBounds: (string.startIndex, string.index(after: string.startIndex))),
|
||||
with: String(string[string.startIndex]).uppercased()
|
||||
)
|
||||
}
|
||||
for (attributeName, customGetterSetters) in (customGetterSetterByKeyPaths ?? [:])
|
||||
where customGetterSetters.getter != nil || customGetterSetters.setter != nil {
|
||||
|
||||
if let getter = customGetterSetters.getter {
|
||||
|
||||
let getterName = "\(attributeName)"
|
||||
guard class_addMethod(
|
||||
managedObjectClass,
|
||||
NSSelectorFromString(getterName),
|
||||
imp_implementationWithBlock(getter),
|
||||
"@@:") else {
|
||||
|
||||
CoreStore.abort("Could not dynamically add getter method \"\(getterName)\" to class \(cs_typeName(managedObjectClass))")
|
||||
}
|
||||
}
|
||||
if let setter = customGetterSetters.setter {
|
||||
|
||||
let setterName = "set\(capitalize(attributeName)):"
|
||||
guard class_addMethod(
|
||||
managedObjectClass,
|
||||
NSSelectorFromString(setterName),
|
||||
imp_implementationWithBlock(setter),
|
||||
"v@:@") else {
|
||||
|
||||
CoreStore.abort("Could not dynamically add setter method \"\(setterName)\" to class \(cs_typeName(managedObjectClass))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let newSelector = NSSelectorFromString("cs_keyPathsForValuesAffectingValueForKey:")
|
||||
let keyPathsByAffectedKeyPaths = entityDescription.keyPathsByAffectedKeyPaths
|
||||
let keyPathsForValuesAffectingValue: @convention(block) (Any, String) -> Set<String> = { (instance, keyPath) in
|
||||
|
||||
if let keyPaths = keyPathsByAffectedKeyPaths[keyPath] {
|
||||
|
||||
return keyPaths
|
||||
}
|
||||
return []
|
||||
}
|
||||
let origSelector = #selector(NSManagedObject.keyPathsForValuesAffectingValue(forKey:))
|
||||
|
||||
let metaClass: AnyClass = object_getClass(managedObjectClass)!
|
||||
let origMethod = class_getClassMethod(managedObjectClass, origSelector)
|
||||
|
||||
let origImp = method_getImplementation(origMethod)
|
||||
let newImp = imp_implementationWithBlock(keyPathsForValuesAffectingValue)
|
||||
|
||||
if class_addMethod(metaClass, origSelector, newImp, method_getTypeEncoding(origMethod)) {
|
||||
|
||||
class_replaceMethod(metaClass, newSelector, origImp, method_getTypeEncoding(origMethod))
|
||||
}
|
||||
else {
|
||||
|
||||
let newMethod = class_getClassMethod(managedObjectClass, newSelector)
|
||||
method_exchangeImplementations(origMethod, newMethod)
|
||||
}
|
||||
}
|
||||
for (dynamicEntity, entityDescription) in entityDescriptionsByEntity {
|
||||
|
||||
createManagedObjectSubclass(
|
||||
for: entityDescription,
|
||||
customGetterSetterByKeyPaths: allCustomGettersSetters[dynamicEntity]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import Foundation
|
||||
/**
|
||||
A `SchemaMappingProvider` that accepts custom mappings for some entities. Mappings of entities with no `CustomMapping` provided will be automatically calculated if possible.
|
||||
*/
|
||||
open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
public class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
|
||||
/**
|
||||
The source model version for the mapping.
|
||||
@@ -352,7 +352,7 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
)
|
||||
func expression(forSource sourceEntity: NSEntityDescription) -> NSExpression {
|
||||
|
||||
return NSExpression(format: "FETCH(FUNCTION($\(NSMigrationManagerKey), \"fetchRequestForSourceEntityNamed:predicateString:\" , \"\(sourceEntity.name!)\", \"\(NSPredicate(value: true))\"), $\(NSMigrationManagerKey).\(#keyPath(NSMigrationManager.sourceContext)), \(false))")
|
||||
return NSExpression(format: "FETCH(FUNCTION($\(NSMigrationManagerKey), \"fetchRequestForSourceEntityNamed:predicateString:\" , \"\(sourceEntity.name!)\", \"\(NSPredicate(value: true))\"), FUNCTION($\(NSMigrationManagerKey), \"\(#selector(getter: NSMigrationManager.sourceContext))\"), \(false))")
|
||||
}
|
||||
|
||||
let sourceEntitiesByName = sourceModel.entitiesByName
|
||||
@@ -427,22 +427,25 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
let destinationAttribute = destination.attribute
|
||||
let propertyMapping = NSPropertyMapping()
|
||||
propertyMapping.name = destinationAttribute.name
|
||||
propertyMapping.valueExpression = NSExpression(format: "$\(NSMigrationSourceObjectKey).\(sourceAttribute.name)")
|
||||
propertyMapping.valueExpression = NSExpression(format: "FUNCTION($\(NSMigrationSourceObjectKey), \"\(#selector(NSManagedObject.value(forKey:)))\", \"\(sourceAttribute.name)\")")
|
||||
attributeMappings.append(propertyMapping)
|
||||
}
|
||||
return attributeMappings
|
||||
}
|
||||
let entityMappingName = entityMapping.name!
|
||||
entityMapping.relationshipMappings = autoreleasepool { () -> [NSPropertyMapping] in
|
||||
|
||||
let sourceRelationships = sourceEntity.cs_resolvedRelationshipRenamingIdentities()
|
||||
let destinationRelationships = destinationEntity.cs_resolvedRelationshipRenamingIdentities()
|
||||
var relationshipMappings: [NSPropertyMapping] = []
|
||||
for (_, destination) in destinationRelationships {
|
||||
for (renamingIdentifier, destination) in destinationRelationships {
|
||||
|
||||
let sourceRelationship = sourceRelationships[renamingIdentifier]!.relationship
|
||||
let destinationRelationship = destination.relationship
|
||||
let sourceRelationshipName = sourceRelationship.name
|
||||
|
||||
let propertyMapping = NSPropertyMapping()
|
||||
propertyMapping.name = destinationRelationship.name
|
||||
propertyMapping.valueExpression = NSExpression(format: "FUNCTION($\(NSMigrationManagerKey), \"\(#selector(NSMigrationManager.destinationInstances(forEntityMappingName:sourceInstances:)))\" , \"\(entityMappingName)\", $\(NSMigrationSourceObjectKey))[0]")
|
||||
propertyMapping.valueExpression = NSExpression(format: "FUNCTION($\(NSMigrationManagerKey), \"destinationInstancesForSourceRelationshipNamed:sourceInstances:\", \"\(sourceRelationshipName)\", FUNCTION($\(NSMigrationSourceObjectKey), \"\(#selector(NSManagedObject.value(forKey:)))\", \"\(sourceRelationshipName)\"))")
|
||||
relationshipMappings.append(propertyMapping)
|
||||
}
|
||||
return relationshipMappings
|
||||
@@ -483,17 +486,24 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
}
|
||||
userInfo[CustomEntityMigrationPolicy.UserInfoKey.sourceAttributesByDestinationKey] = sourceAttributesByDestinationKey
|
||||
}
|
||||
let entityMappingName = entityMapping.name!
|
||||
entityMapping.relationshipMappings = autoreleasepool { () -> [NSPropertyMapping] in
|
||||
|
||||
let sourceRelationships = sourceEntity.cs_resolvedRelationshipRenamingIdentities()
|
||||
let destinationRelationships = destinationEntity.cs_resolvedRelationshipRenamingIdentities()
|
||||
let transformedRenamingIdentifiers = Set(destinationRelationships.keys)
|
||||
.intersection(sourceRelationships.keys)
|
||||
|
||||
var relationshipMappings: [NSPropertyMapping] = []
|
||||
for (_, destination) in destinationRelationships {
|
||||
|
||||
let destinationRelationship = destination.relationship
|
||||
for renamingIdentifier in transformedRenamingIdentifiers {
|
||||
|
||||
let sourceRelationship = sourceRelationships[renamingIdentifier]!.relationship
|
||||
let destinationRelationship = destinationRelationships[renamingIdentifier]!.relationship
|
||||
let sourceRelationshipName = sourceRelationship.name
|
||||
let destinationRelationshipName = destinationRelationship.name
|
||||
|
||||
let propertyMapping = NSPropertyMapping()
|
||||
propertyMapping.name = destinationRelationship.name
|
||||
propertyMapping.valueExpression = NSExpression(format: "FUNCTION($\(NSMigrationManagerKey), \"\(#selector(NSMigrationManager.destinationInstances(forEntityMappingName:sourceInstances:)))\" , \"\(entityMappingName)\", $\(NSMigrationSourceObjectKey))[0]")
|
||||
propertyMapping.name = destinationRelationshipName
|
||||
propertyMapping.valueExpression = NSExpression(format: "FUNCTION($\(NSMigrationManagerKey), \"destinationInstancesForSourceRelationshipNamed:sourceInstances:\", \"\(sourceRelationshipName)\", FUNCTION($\(NSMigrationSourceObjectKey), \"\(#selector(NSManagedObject.value(forKey:)))\", \"\(sourceRelationshipName)\"))")
|
||||
relationshipMappings.append(propertyMapping)
|
||||
}
|
||||
return relationshipMappings
|
||||
@@ -545,11 +555,7 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
)
|
||||
if let dInstance = destinationObject?.rawObject {
|
||||
|
||||
manager.associate(
|
||||
sourceInstance: sInstance,
|
||||
withDestinationInstance: dInstance,
|
||||
for: mapping
|
||||
)
|
||||
manager.associate(sourceInstance: sInstance, withDestinationInstance: dInstance, for: mapping)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,77 +676,76 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
allMappedSourceKeys[sourceEntity] = destinationEntity
|
||||
allMappedDestinationKeys[destinationEntity] = sourceEntity
|
||||
}
|
||||
}
|
||||
for renamingIdentifier in transformedRenamingIdentifiers {
|
||||
|
||||
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]) {
|
||||
|
||||
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):
|
||||
if sourceEntity.versionHash == destinationEntity.versionHash {
|
||||
|
||||
case (nil, nil):
|
||||
if sourceEntity.versionHash == destinationEntity.versionHash {
|
||||
|
||||
copyMappings.insert(
|
||||
.copyEntity(
|
||||
sourceEntity: sourceEntityName,
|
||||
destinationEntity: destinationEntityName
|
||||
)
|
||||
copyMappings.insert(
|
||||
.copyEntity(
|
||||
sourceEntity: sourceEntityName,
|
||||
destinationEntity: destinationEntityName
|
||||
)
|
||||
}
|
||||
else {
|
||||
|
||||
transformMappings.insert(
|
||||
.transformEntity(
|
||||
sourceEntity: sourceEntityName,
|
||||
destinationEntity: destinationEntityName,
|
||||
transformer: CustomMapping.inferredTransformation
|
||||
)
|
||||
)
|
||||
}
|
||||
else {
|
||||
|
||||
transformMappings.insert(
|
||||
.transformEntity(
|
||||
sourceEntity: sourceEntityName,
|
||||
destinationEntity: destinationEntityName,
|
||||
transformer: 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 {
|
||||
allMappedSourceKeys[sourceEntityName] = destinationEntityName
|
||||
allMappedDestinationKeys[destinationEntityName] = sourceEntityName
|
||||
|
||||
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 {
|
||||
case (""?, nil):
|
||||
insertMappings.insert(.insertEntity(destinationEntity: destinationEntityName))
|
||||
allMappedDestinationKeys[destinationEntityName] = ""
|
||||
|
||||
let destinationEntity = destinationRenamingIdentifiers[renamingIdentifier]!.entity
|
||||
let destinationEntityName = destinationEntity.name!
|
||||
switch allMappedDestinationKeys[destinationEntityName] {
|
||||
|
||||
case nil:
|
||||
insertMappings.insert(.insertEntity(destinationEntity: destinationEntityName))
|
||||
allMappedDestinationKeys[destinationEntityName] = ""
|
||||
|
||||
default:
|
||||
continue
|
||||
}
|
||||
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, copyMappings, transformMappings)
|
||||
|
||||
@@ -707,6 +707,7 @@ public extension DataStack {
|
||||
|
||||
do {
|
||||
|
||||
try storage.cs_finalizeStorageAndWait(soureModelHint: sourceModel)
|
||||
try migrationManager.migrateStore(
|
||||
from: fileURL,
|
||||
sourceType: type(of: storage).storeType,
|
||||
@@ -716,6 +717,13 @@ public extension DataStack {
|
||||
destinationType: type(of: storage).storeType,
|
||||
destinationOptions: nil
|
||||
)
|
||||
let temporaryStorage = SQLiteStore(
|
||||
fileURL: temporaryFileURL,
|
||||
configuration: storage.configuration,
|
||||
migrationMappingProviders: storage.migrationMappingProviders,
|
||||
localStorageOptions: storage.localStorageOptions
|
||||
)
|
||||
try temporaryStorage.cs_finalizeStorageAndWait(soureModelHint: destinationModel)
|
||||
}
|
||||
catch {
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ public extension DataStack {
|
||||
}
|
||||
|
||||
/**
|
||||
Performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be rethrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
Performs a transaction synchronously where `NSManagedObject` or `CoreStoreObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. On success, the value returned from closure will be the return value of `perform(synchronous:)`. Any errors thrown from inside the `task` will be thrown from `perform(synchronous:)`. To cancel/rollback changes, call `try transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
|
||||
|
||||
- parameter task: the synchronous non-escaping closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
|
||||
- parameter waitForAllObservers: When `true`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks. When `false`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning. Defaults to `true`.
|
||||
|
||||
@@ -155,14 +155,3 @@ extension CoreStoreObject {
|
||||
return self.rawObject!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
internal extension DynamicObject where Self: CoreStoreObject {
|
||||
|
||||
internal static var meta: Self {
|
||||
|
||||
return self.init(asMeta: ())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,71 +96,62 @@ public extension DynamicSchema {
|
||||
|
||||
case .integer16AttributeType:
|
||||
valueType = Int16.self
|
||||
if let defaultValue = (attribute.defaultValue as! Int16.ImportableNativeType?).flatMap(Int16.cs_fromImportableNativeType),
|
||||
defaultValue != Int16.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! Int16.ImportableNativeType?).flatMap(Int16.cs_fromImportableNativeType) {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
defaultString = ", initial: \(defaultValue)"
|
||||
}
|
||||
case .integer32AttributeType:
|
||||
valueType = Int32.self
|
||||
if let defaultValue = (attribute.defaultValue as! Int32.ImportableNativeType?).flatMap(Int32.cs_fromImportableNativeType),
|
||||
defaultValue != Int32.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! Int32.ImportableNativeType?).flatMap(Int32.cs_fromImportableNativeType) {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
defaultString = ", initial: \(defaultValue)"
|
||||
}
|
||||
case .integer64AttributeType:
|
||||
valueType = Int64.self
|
||||
if let defaultValue = (attribute.defaultValue as! Int64.ImportableNativeType?).flatMap(Int64.cs_fromImportableNativeType),
|
||||
defaultValue != Int64.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! Int64.ImportableNativeType?).flatMap(Int64.cs_fromImportableNativeType) {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
defaultString = ", initial: \(defaultValue)"
|
||||
}
|
||||
case .decimalAttributeType:
|
||||
valueType = NSDecimalNumber.self
|
||||
if let defaultValue = (attribute.defaultValue as! NSDecimalNumber.ImportableNativeType?).flatMap(NSDecimalNumber.cs_fromImportableNativeType),
|
||||
defaultValue != NSDecimalNumber.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! NSDecimalNumber?) {
|
||||
|
||||
defaultString = ", default: NSDecimalNumber(string: \"\(defaultValue.description(withLocale: nil))\")"
|
||||
defaultString = ", initial: NSDecimalNumber(string: \"\(defaultValue.description(withLocale: nil))\")"
|
||||
}
|
||||
case .doubleAttributeType:
|
||||
valueType = Double.self
|
||||
if let defaultValue = (attribute.defaultValue as! Double.ImportableNativeType?).flatMap(Double.cs_fromImportableNativeType),
|
||||
defaultValue != Double.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! Double.ImportableNativeType?).flatMap(Double.cs_fromImportableNativeType) {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
defaultString = ", initial: \(defaultValue)"
|
||||
}
|
||||
case .floatAttributeType:
|
||||
valueType = Float.self
|
||||
if let defaultValue = (attribute.defaultValue as! Float.ImportableNativeType?).flatMap(Float.cs_fromImportableNativeType),
|
||||
defaultValue != Float.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! Float.ImportableNativeType?).flatMap(Float.cs_fromImportableNativeType) {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
defaultString = ", initial: \(defaultValue)"
|
||||
}
|
||||
case .stringAttributeType:
|
||||
valueType = String.self
|
||||
if let defaultValue = (attribute.defaultValue as! String.ImportableNativeType?).flatMap(String.cs_fromImportableNativeType),
|
||||
defaultValue != String.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! String.ImportableNativeType?).flatMap(String.cs_fromImportableNativeType) {
|
||||
|
||||
// TODO: escape strings
|
||||
defaultString = ", default: \"\(defaultValue)\""
|
||||
defaultString = ", initial: \"\(defaultValue)\""
|
||||
}
|
||||
case .booleanAttributeType:
|
||||
valueType = Bool.self
|
||||
if let defaultValue = (attribute.defaultValue as! Bool.ImportableNativeType?).flatMap(Bool.cs_fromImportableNativeType),
|
||||
defaultValue != Bool.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! Bool.ImportableNativeType?).flatMap(Bool.cs_fromImportableNativeType) {
|
||||
|
||||
defaultString = ", default: \(defaultValue ? "true" : "false")"
|
||||
defaultString = ", initial: \(defaultValue ? "true" : "false")"
|
||||
}
|
||||
case .dateAttributeType:
|
||||
valueType = Date.self
|
||||
if let defaultValue = (attribute.defaultValue as! Date.ImportableNativeType?).flatMap(Date.cs_fromImportableNativeType) {
|
||||
|
||||
defaultString = ", default: Date(timeIntervalSinceReferenceDate: \(defaultValue.timeIntervalSinceReferenceDate))"
|
||||
defaultString = ", initial: Date(timeIntervalSinceReferenceDate: \(defaultValue.timeIntervalSinceReferenceDate))"
|
||||
}
|
||||
case .binaryDataAttributeType:
|
||||
valueType = Data.self
|
||||
if let defaultValue = (attribute.defaultValue as! Data.ImportableNativeType?).flatMap(Data.cs_fromImportableNativeType),
|
||||
defaultValue != Data.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! Data.ImportableNativeType?).flatMap(Data.cs_fromImportableNativeType) {
|
||||
|
||||
let count = defaultValue.count
|
||||
let bytes = defaultValue.withUnsafeBytes { (pointer: UnsafePointer<UInt8>) in
|
||||
@@ -168,7 +159,7 @@ public extension DynamicSchema {
|
||||
return (0 ..< (count / MemoryLayout<UInt8>.size))
|
||||
.map({ "\("0x\(String(pointer[$0], radix: 16, uppercase: false))")" })
|
||||
}
|
||||
defaultString = ", default: Data(bytes: [\(bytes.joined(separator: ", "))])"
|
||||
defaultString = ", initial: Data(bytes: [\(bytes.joined(separator: ", "))])"
|
||||
}
|
||||
case .transformableAttributeType:
|
||||
if let attributeValueClassName = attribute.attributeValueClassName {
|
||||
@@ -181,11 +172,11 @@ public extension DynamicSchema {
|
||||
}
|
||||
if let defaultValue = attribute.defaultValue {
|
||||
|
||||
defaultString = ", default: /* \"\(defaultValue)\" */"
|
||||
defaultString = ", initial: /* \"\(defaultValue)\" */"
|
||||
}
|
||||
else if !attribute.isOptional {
|
||||
|
||||
defaultString = ", default: /* required */"
|
||||
defaultString = ", initial: /* required */"
|
||||
}
|
||||
default:
|
||||
fatalError("Unsupported attribute type: \(attribute.attributeType.rawValue)")
|
||||
|
||||
@@ -78,26 +78,9 @@ public protocol ImportableAttributeType: QueryableAttributeType {
|
||||
}
|
||||
|
||||
|
||||
// MARK: - EmptyableAttributeType
|
||||
|
||||
/**
|
||||
`ImportableAttributeType`s that have a natural "empty" value. Example: `0` for `Int`, `""` for `String`.
|
||||
|
||||
- Discussion: Not all `ImportableAttributeType`s can have empty values. `URL`s and `Date`s for example have no obvious empty values.
|
||||
*/
|
||||
public protocol EmptyableAttributeType: ImportableAttributeType {
|
||||
|
||||
/**
|
||||
Returns the default "empty" value for this type.
|
||||
*/
|
||||
@inline(__always)
|
||||
static func cs_emptyValue() -> Self
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Bool
|
||||
|
||||
extension Bool: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension Bool: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -114,21 +97,11 @@ extension Bool: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Bool {
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CGFloat
|
||||
|
||||
extension CGFloat: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension CGFloat: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -145,21 +118,12 @@ extension CGFloat: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> CGFloat {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
extension Data: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension Data: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -176,15 +140,6 @@ extension Data: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Data {
|
||||
|
||||
return Data()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +167,7 @@ extension Date: ImportableAttributeType {
|
||||
|
||||
// MARK: - Double
|
||||
|
||||
extension Double: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension Double: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -229,21 +184,12 @@ extension Double: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Double {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Float
|
||||
|
||||
extension Float: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension Float: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -260,21 +206,12 @@ extension Float: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Float {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int
|
||||
|
||||
extension Int: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension Int: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -291,21 +228,12 @@ extension Int: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int8
|
||||
|
||||
extension Int8: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension Int8: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -322,21 +250,12 @@ extension Int8: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int8 {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int16
|
||||
|
||||
extension Int16: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension Int16: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -353,21 +272,12 @@ extension Int16: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int16 {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int32
|
||||
|
||||
extension Int32: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension Int32: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -384,21 +294,12 @@ extension Int32: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int32 {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int64
|
||||
|
||||
extension Int64: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension Int64: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -415,21 +316,12 @@ extension Int64: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int64 {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSData
|
||||
|
||||
extension NSData: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension NSData: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -446,15 +338,6 @@ extension NSData: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -482,7 +365,7 @@ extension NSDate: ImportableAttributeType {
|
||||
|
||||
// MARK: - NSNumber
|
||||
|
||||
extension NSNumber: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension NSNumber: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -499,21 +382,12 @@ extension NSNumber: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSString
|
||||
|
||||
extension NSString: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension NSString: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -530,15 +404,6 @@ extension NSString: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -588,7 +453,7 @@ extension NSUUID: ImportableAttributeType {
|
||||
|
||||
// MARK: - String
|
||||
|
||||
extension String: ImportableAttributeType, EmptyableAttributeType {
|
||||
extension String: ImportableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@@ -605,15 +470,6 @@ extension String: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> String {
|
||||
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import Foundation
|
||||
/**
|
||||
A `SchemaMappingProvider` that tries to infer model migration between two `DynamicSchema` versions by searching all `xcmappingmodel`s from `Bundle.allBundles` or by relying on lightweight migration if possible. Throws an error if lightweight migration is impossible for the two `DynamicSchema`. This mapping is automatically used as a fallback mapping provider, even if no mapping providers are explicitly declared in the `StorageInterface`.
|
||||
*/
|
||||
final class InferredSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
public final class InferredSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.0.0</string>
|
||||
<string>4.1.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -86,6 +86,11 @@ public final class LegacySQLiteStore: LocalStorage {
|
||||
|
||||
public var localStorageOptions: LocalStorageOptions
|
||||
|
||||
public func cs_finalizeStorageAndWait(soureModelHint: NSManagedObjectModel) throws {
|
||||
|
||||
fatalError()
|
||||
}
|
||||
|
||||
public func cs_eraseStorageAndWait(metadata: [String: Any], soureModelHint: NSManagedObjectModel?) throws {
|
||||
|
||||
fatalError()
|
||||
|
||||
@@ -704,13 +704,13 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
||||
|
||||
guard let `self` = self,
|
||||
let userInfo = note.userInfo,
|
||||
let object = userInfo[String(describing: NSManagedObject.self)] as? ObjectType else {
|
||||
let rawObject = userInfo[String(describing: NSManagedObject.self)] as? NSManagedObject else {
|
||||
|
||||
return
|
||||
}
|
||||
callback(
|
||||
self,
|
||||
object,
|
||||
ObjectType.cs_fromRaw(object: rawObject),
|
||||
userInfo[String(describing: IndexPath.self)] as? IndexPath,
|
||||
userInfo["\(String(describing: IndexPath.self)).New"] as? IndexPath
|
||||
)
|
||||
|
||||
@@ -54,17 +54,65 @@ internal extension NSEntityDescription {
|
||||
|
||||
if let newValue = newValue {
|
||||
|
||||
var userInfo: [AnyHashable : Any] = [
|
||||
UserInfoKey.CoreStoreManagedObjectTypeName: NSStringFromClass(newValue.type),
|
||||
UserInfoKey.CoreStoreManagedObjectEntityName: newValue.entityName,
|
||||
UserInfoKey.CoreStoreManagedObjectIsAbstract: newValue.isAbstract
|
||||
]
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectVersionHashModifier] = newValue.versionHashModifier
|
||||
self.userInfo = userInfo
|
||||
cs_setUserInfo { (userInfo) in
|
||||
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectTypeName] = NSStringFromClass(newValue.type)
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectEntityName] = newValue.entityName
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectIsAbstract] = newValue.isAbstract
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectVersionHashModifier] = newValue.versionHashModifier
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
self.userInfo = [:]
|
||||
cs_setUserInfo { (userInfo) in
|
||||
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectTypeName] = nil
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectEntityName] = nil
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectIsAbstract] = nil
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectVersionHashModifier] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal var keyPathsByAffectedKeyPaths: [KeyPath: Set<KeyPath>] {
|
||||
|
||||
get {
|
||||
|
||||
if let userInfo = self.userInfo,
|
||||
let value = userInfo[UserInfoKey.CoreStoreManagedObjectKeyPathsByAffectedKeyPaths] {
|
||||
|
||||
return value as! [KeyPath: Set<KeyPath>]
|
||||
}
|
||||
return [:]
|
||||
}
|
||||
set {
|
||||
|
||||
cs_setUserInfo { (userInfo) in
|
||||
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectKeyPathsByAffectedKeyPaths] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal var customGetterSetterByKeyPaths: [KeyPath: CoreStoreManagedObject.CustomGetterSetter] {
|
||||
|
||||
get {
|
||||
|
||||
if let userInfo = self.userInfo,
|
||||
let value = userInfo[UserInfoKey.CoreStoreManagedObjectCustomGetterSetterByKeyPaths] {
|
||||
|
||||
return value as! [KeyPath: CoreStoreManagedObject.CustomGetterSetter]
|
||||
}
|
||||
return [:]
|
||||
}
|
||||
set {
|
||||
|
||||
cs_setUserInfo { (userInfo) in
|
||||
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectCustomGetterSetterByKeyPaths] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,11 +122,22 @@ internal extension NSEntityDescription {
|
||||
|
||||
// MARK: - UserInfoKey
|
||||
|
||||
fileprivate enum UserInfoKey {
|
||||
private enum UserInfoKey {
|
||||
|
||||
fileprivate static let CoreStoreManagedObjectTypeName = "CoreStoreManagedObjectTypeName"
|
||||
fileprivate static let CoreStoreManagedObjectEntityName = "CoreStoreManagedObjectEntityName"
|
||||
fileprivate static let CoreStoreManagedObjectIsAbstract = "CoreStoreManagedObjectIsAbstract"
|
||||
fileprivate static let CoreStoreManagedObjectVersionHashModifier = "CoreStoreManagedObjectVersionHashModifier"
|
||||
|
||||
fileprivate static let CoreStoreManagedObjectKeyPathsByAffectedKeyPaths = "CoreStoreManagedObjectKeyPathsByAffectedKeyPaths"
|
||||
fileprivate static let CoreStoreManagedObjectCustomGetterSetterByKeyPaths = "CoreStoreManagedObjectCustomGetterSetterByKeyPaths"
|
||||
|
||||
}
|
||||
|
||||
private func cs_setUserInfo(_ closure: (_ userInfo: inout [AnyHashable: Any]) -> Void) {
|
||||
|
||||
var userInfo = self.userInfo ?? [:]
|
||||
closure(&userInfo)
|
||||
self.userInfo = userInfo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ public extension UnsafeDataTransaction {
|
||||
// MARK: - Private
|
||||
|
||||
@available(OSX 10.12, *)
|
||||
fileprivate func createFRC<T: NSManagedObject>(fromContext context: NSManagedObjectContext, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController<T> {
|
||||
fileprivate func createFRC<T: NSManagedObject>(fromContext context: NSManagedObjectContext, from: From<T>, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController<T> {
|
||||
|
||||
let controller = CoreStoreFetchedResultsController(
|
||||
context: context,
|
||||
|
||||
@@ -68,12 +68,12 @@ public extension CSUnsafeDataTransaction {
|
||||
// MARK: - Private
|
||||
|
||||
@available(OSX 10.12, *)
|
||||
fileprivate func createFRC(fromContext context: NSManagedObjectContext, from: CSFrom? = nil, sectionBy: CSSectionBy?, fetchClauses: [CSFetchClause]) -> NSFetchedResultsController<NSManagedObject> {
|
||||
fileprivate func createFRC(fromContext context: NSManagedObjectContext, from: CSFrom, sectionBy: CSSectionBy?, fetchClauses: [CSFetchClause]) -> NSFetchedResultsController<NSManagedObject> {
|
||||
|
||||
let controller = CoreStoreFetchedResultsController(
|
||||
context: context,
|
||||
fetchRequest: CoreStoreFetchRequest().dynamicCast(),
|
||||
from: from?.bridgeToSwift,
|
||||
from: from.bridgeToSwift,
|
||||
sectionBy: sectionBy?.bridgeToSwift,
|
||||
applyFetchClauses: { (fetchRequest) in
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ extension NSManagedObjectContext: FetchableSource, QueryableSource {
|
||||
|
||||
return object
|
||||
}
|
||||
return T.cs_fromRaw(object: existingRawObject)
|
||||
return type(of: object).cs_fromRaw(object: existingRawObject)
|
||||
}
|
||||
catch {
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
||||
let fetchedResultsController = CoreStoreFetchedResultsController(
|
||||
context: context,
|
||||
fetchRequest: fetchRequest.dynamicCast(),
|
||||
from: nil as From<ObjectType>?,
|
||||
from: From<ObjectType>([objectID.persistentStore?.configurationName]),
|
||||
applyFetchClauses: Where("SELF", isEqualTo: objectID).applyToFetchRequest
|
||||
)
|
||||
|
||||
|
||||
@@ -172,3 +172,17 @@ public struct OrderBy: FetchClause, QueryClause, DeleteClause, Hashable {
|
||||
return (self.sortDescriptors as NSArray).hashValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Sequence where Element == OrderBy
|
||||
|
||||
public extension Sequence where Iterator.Element == OrderBy {
|
||||
|
||||
/**
|
||||
Combines multiple `OrderBy` predicates together
|
||||
*/
|
||||
public func combined() -> OrderBy {
|
||||
|
||||
return OrderBy(self.flatMap({ $0.sortDescriptors }))
|
||||
}
|
||||
}
|
||||
|
||||
173
Sources/PartialObject.swift
Normal file
173
Sources/PartialObject.swift
Normal file
@@ -0,0 +1,173 @@
|
||||
//
|
||||
// PartialObject.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: - PartialObject
|
||||
|
||||
/**
|
||||
A `PartialObject` is only used when overriding getters and setters for `CoreStoreObject` properties. Custom getters and setters are implemented as a closure that "overrides" the default property getter/setter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a heavy performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.persistentValue(for:)` instead of `PartialObject<O>.value(for:)`, which would unintentionally execute the same closure again recursively.
|
||||
*/
|
||||
public struct PartialObject<O: CoreStoreObject> {
|
||||
|
||||
public func completeObject() -> O {
|
||||
|
||||
return O.cs_fromRaw(object: self.rawObject)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Value.Required accessors/mutators
|
||||
|
||||
public func value<V: ImportableAttributeType>(for property: (O) -> ValueContainer<O>.Required<V>) -> V {
|
||||
|
||||
return V.cs_fromImportableNativeType(
|
||||
self.rawObject.value(forKey: property(O.meta).keyPath)! as! V.ImportableNativeType
|
||||
)!
|
||||
}
|
||||
|
||||
public func setValue<V: ImportableAttributeType>(_ value: V, for property: (O) -> ValueContainer<O>.Required<V>) {
|
||||
|
||||
self.rawObject.setValue(
|
||||
value.cs_toImportableNativeType(),
|
||||
forKey: property(O.meta).keyPath
|
||||
)
|
||||
}
|
||||
|
||||
public func primitiveValue<V: ImportableAttributeType>(for property: (O) -> ValueContainer<O>.Required<V>) -> V {
|
||||
|
||||
return V.cs_fromImportableNativeType(
|
||||
self.rawObject.primitiveValue(forKey: property(O.meta).keyPath)! as! V.ImportableNativeType
|
||||
)!
|
||||
}
|
||||
|
||||
public func setPrimitiveValue<V: ImportableAttributeType>(_ value: V, for property: (O) -> ValueContainer<O>.Required<V>) {
|
||||
|
||||
self.rawObject.setPrimitiveValue(
|
||||
value.cs_toImportableNativeType(),
|
||||
forKey: property(O.meta).keyPath
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Value.Optional utilities
|
||||
|
||||
public func value<V: ImportableAttributeType>(for property: (O) -> ValueContainer<O>.Optional<V>) -> V? {
|
||||
|
||||
return (self.rawObject.value(forKey: property(O.meta).keyPath) as! V.ImportableNativeType?)
|
||||
.flatMap(V.cs_fromImportableNativeType)
|
||||
}
|
||||
|
||||
public func setValue<V: ImportableAttributeType>(_ value: V?, for property: (O) -> ValueContainer<O>.Optional<V>) {
|
||||
|
||||
self.rawObject.setValue(
|
||||
value?.cs_toImportableNativeType(),
|
||||
forKey: property(O.meta).keyPath
|
||||
)
|
||||
}
|
||||
|
||||
public func primitiveValue<V: ImportableAttributeType>(for property: (O) -> ValueContainer<O>.Optional<V>) -> V? {
|
||||
|
||||
return (self.rawObject.primitiveValue(forKey: property(O.meta).keyPath) as! V.ImportableNativeType?)
|
||||
.flatMap(V.cs_fromImportableNativeType)
|
||||
}
|
||||
|
||||
public func setPrimitiveValue<V: ImportableAttributeType>(_ value: V?, for property: (O) -> ValueContainer<O>.Optional<V>) {
|
||||
|
||||
self.rawObject.setPrimitiveValue(
|
||||
value?.cs_toImportableNativeType(),
|
||||
forKey: property(O.meta).keyPath
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Transformable.Required utilities
|
||||
|
||||
public func value<V: NSCoding & NSCopying>(for property: (O) -> TransformableContainer<O>.Required<V>) -> V {
|
||||
|
||||
return self.rawObject.value(forKey: property(O.meta).keyPath)! as! V
|
||||
}
|
||||
|
||||
public func setValue<V: NSCoding & NSCopying>(_ value: V, for property: (O) -> TransformableContainer<O>.Required<V>) {
|
||||
|
||||
self.rawObject.setValue(
|
||||
value,
|
||||
forKey: property(O.meta).keyPath
|
||||
)
|
||||
}
|
||||
|
||||
public func primitiveValue<V: NSCoding & NSCopying>(for property: (O) -> TransformableContainer<O>.Required<V>) -> V {
|
||||
|
||||
return self.rawObject.primitiveValue(forKey: property(O.meta).keyPath)! as! V
|
||||
}
|
||||
|
||||
public func setPrimitiveValue<V: NSCoding & NSCopying>(_ value: V, for property: (O) -> TransformableContainer<O>.Required<V>) {
|
||||
|
||||
self.rawObject.setPrimitiveValue(
|
||||
value,
|
||||
forKey: property(O.meta).keyPath
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Transformable.Optional utilities
|
||||
|
||||
public func value<V: NSCoding & NSCopying>(for property: (O) -> TransformableContainer<O>.Optional<V>) -> V? {
|
||||
|
||||
return self.rawObject.value(forKey: property(O.meta).keyPath) as! V?
|
||||
}
|
||||
|
||||
public func setValue<V: NSCoding & NSCopying>(_ value: V?, for property: (O) -> TransformableContainer<O>.Optional<V>) {
|
||||
|
||||
self.rawObject.setValue(
|
||||
value,
|
||||
forKey: property(O.meta).keyPath
|
||||
)
|
||||
}
|
||||
|
||||
public func primitiveValue<V: NSCoding & NSCopying>(for property: (O) -> TransformableContainer<O>.Optional<V>) -> V? {
|
||||
|
||||
return self.rawObject.primitiveValue(forKey: property(O.meta).keyPath) as! V?
|
||||
}
|
||||
|
||||
public func setPrimitiveValue<V: NSCoding & NSCopying>(_ value: V?, for property: (O) -> TransformableContainer<O>.Optional<V>) {
|
||||
|
||||
self.rawObject.setPrimitiveValue(
|
||||
value,
|
||||
forKey: property(O.meta).keyPath
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal let rawObject: NSManagedObject
|
||||
|
||||
internal init(_ rawObject: NSManagedObject) {
|
||||
|
||||
self.rawObject = rawObject
|
||||
}
|
||||
}
|
||||
@@ -92,10 +92,23 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
inverseKeyPath: { nil },
|
||||
deleteRule: deleteRule,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,10 +126,24 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
inverseKeyPath: { inverse(D.meta).keyPath },
|
||||
deleteRule: deleteRule,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,10 +161,24 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
inverseKeyPath: { inverse(D.meta).keyPath },
|
||||
deleteRule: deleteRule,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,10 +196,24 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
inverseKeyPath: { inverse(D.meta).keyPath },
|
||||
deleteRule: deleteRule,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,56 +242,66 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
internal let minCount: Int = 0
|
||||
internal let maxCount: Int = 1
|
||||
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
||||
internal let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var parentObject: () -> CoreStoreObject = {
|
||||
|
||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
||||
}
|
||||
internal let versionHashModifier: () -> String?
|
||||
internal let renamingIdentifier: () -> String?
|
||||
internal let affectedByKeyPaths: () -> Set<String>
|
||||
internal weak var parentObject: CoreStoreObject?
|
||||
|
||||
internal var nativeValue: NSManagedObject? {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { $0 as! NSManagedObject? }
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { $0 as! NSManagedObject? }
|
||||
)
|
||||
}
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, versionHashModifier: String?, renamingIdentifier: String?) {
|
||||
private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, versionHashModifier: @autoclosure @escaping () -> String?, renamingIdentifier: @autoclosure @escaping () -> String?, affectedByKeyPaths: @autoclosure @escaping () -> Set<String>) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.deleteRule = deleteRule.nativeValue
|
||||
self.inverse = (D.self, inverseKeyPath)
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.affectedByKeyPaths = affectedByKeyPaths
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,10 +338,27 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, minCount: Int = 0, maxCount: Int = 0, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, minCount: minCount, maxCount: maxCount, inverseKeyPath: { nil }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount,
|
||||
inverseKeyPath: { nil },
|
||||
deleteRule: deleteRule,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,10 +378,28 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, minCount: Int = 0, maxCount: Int = 0, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, minCount: minCount, maxCount: maxCount, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount,
|
||||
inverseKeyPath: { inverse(D.meta).keyPath },
|
||||
deleteRule: deleteRule,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,10 +419,28 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, minCount: Int = 0, maxCount: Int = 0, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, minCount: minCount, maxCount: maxCount, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount,
|
||||
inverseKeyPath: { inverse(D.meta).keyPath },
|
||||
deleteRule: deleteRule,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,10 +460,28 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, minCount: Int = 0, maxCount: Int = 0, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, minCount: minCount, maxCount: maxCount, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount,
|
||||
inverseKeyPath: { inverse(D.meta).keyPath },
|
||||
deleteRule: deleteRule,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,50 +511,59 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
internal let minCount: Int
|
||||
internal let maxCount: Int
|
||||
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
||||
internal let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var parentObject: () -> CoreStoreObject = {
|
||||
|
||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
||||
}
|
||||
internal let versionHashModifier: () -> String?
|
||||
internal let renamingIdentifier: () -> String?
|
||||
internal let affectedByKeyPaths: () -> Set<String>
|
||||
internal weak var parentObject: CoreStoreObject?
|
||||
|
||||
internal var nativeValue: NSOrderedSet {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { ($0 as! NSOrderedSet?) ?? [] }
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { ($0 as! NSOrderedSet?) ?? [] }
|
||||
)
|
||||
}
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private init(keyPath: String, minCount: Int, maxCount: Int, inverseKeyPath: @escaping () -> String?, deleteRule: DeleteRule, versionHashModifier: String?, renamingIdentifier: String?) {
|
||||
private init(keyPath: String, minCount: Int, maxCount: Int, inverseKeyPath: @escaping () -> String?, deleteRule: DeleteRule, versionHashModifier: @autoclosure @escaping () -> String?, renamingIdentifier: @autoclosure @escaping () -> String?, affectedByKeyPaths: @autoclosure @escaping () -> Set<String>) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.deleteRule = deleteRule.nativeValue
|
||||
@@ -429,6 +574,7 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
let range = (Swift.max(0, minCount) ... maxCount)
|
||||
self.minCount = range.lowerBound
|
||||
self.maxCount = range.upperBound
|
||||
self.affectedByKeyPaths = affectedByKeyPaths
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,10 +612,27 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
inverseKeyPath: { nil },
|
||||
deleteRule: deleteRule,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,10 +652,28 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
inverseKeyPath: { inverse(D.meta).keyPath },
|
||||
deleteRule: deleteRule,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -512,10 +693,28 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
inverseKeyPath: { inverse(D.meta).keyPath },
|
||||
deleteRule: deleteRule,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -535,10 +734,28 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
- parameter deleteRule: defines what happens to relationship when an object is deleted. Valid values are `.nullify`, `.cascade`, and `.delete`. Defaults to `.nullify`.
|
||||
- parameter versionHashModifier: used to mark or denote a relationship as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0, versionHashModifier: String? = nil, renamingIdentifier: String? = nil) {
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount, versionHashModifier: versionHashModifier, renamingIdentifier: renamingIdentifier)
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
inverseKeyPath: { inverse(D.meta).keyPath },
|
||||
deleteRule: deleteRule,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -568,50 +785,59 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
internal let minCount: Int
|
||||
internal let maxCount: Int
|
||||
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
||||
internal let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var parentObject: () -> CoreStoreObject = {
|
||||
|
||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
||||
}
|
||||
internal let versionHashModifier: () -> String?
|
||||
internal let renamingIdentifier: () -> String?
|
||||
internal let affectedByKeyPaths: () -> Set<String>
|
||||
internal weak var parentObject: CoreStoreObject?
|
||||
|
||||
internal var nativeValue: NSSet {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { ($0 as! NSSet?) ?? [] }
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { ($0 as! NSSet?) ?? [] }
|
||||
)
|
||||
}
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, minCount: Int, maxCount: Int, versionHashModifier: String?, renamingIdentifier: String?) {
|
||||
private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, minCount: Int, maxCount: Int, versionHashModifier: @autoclosure @escaping () -> String?, renamingIdentifier: @autoclosure @escaping () -> String?, affectedByKeyPaths: @autoclosure @escaping () -> Set<String>) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.deleteRule = deleteRule.nativeValue
|
||||
@@ -622,6 +848,7 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
let range = (Swift.max(0, minCount) ... maxCount)
|
||||
self.minCount = range.lowerBound
|
||||
self.maxCount = range.upperBound
|
||||
self.affectedByKeyPaths = affectedByKeyPaths
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,7 +885,7 @@ extension RelationshipContainer.ToManyOrdered: RandomAccessCollection {
|
||||
public func makeIterator() -> Iterator {
|
||||
|
||||
let iterator = self.nativeValue.makeIterator()
|
||||
return AnyIterator({ D.cs_fromRaw(object: iterator.next() as! NSManagedObject) })
|
||||
return AnyIterator({ iterator.next().flatMap({ D.cs_fromRaw(object: $0 as! NSManagedObject) }) })
|
||||
}
|
||||
|
||||
|
||||
@@ -713,7 +940,7 @@ extension RelationshipContainer.ToManyUnordered: Sequence {
|
||||
public func makeIterator() -> Iterator {
|
||||
|
||||
let iterator = self.nativeValue.makeIterator()
|
||||
return AnyIterator({ D.cs_fromRaw(object: iterator.next() as! NSManagedObject) })
|
||||
return AnyIterator({ iterator.next().flatMap({ D.cs_fromRaw(object: $0 as! NSManagedObject) }) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -977,20 +1204,3 @@ extension RelationshipContainer.ToManyUnordered {
|
||||
return relationship.nativeValue.isEqual(relationship2.nativeValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - RelationshipProtocol
|
||||
|
||||
internal protocol RelationshipProtocol: class {
|
||||
|
||||
var keyPath: KeyPath { get }
|
||||
var isToMany: Bool { get }
|
||||
var isOrdered: Bool { get }
|
||||
var deleteRule: NSDeleteRule { get }
|
||||
var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) { get }
|
||||
var parentObject: () -> CoreStoreObject { get set }
|
||||
var versionHashModifier: String? { get }
|
||||
var renamingIdentifier: String? { get }
|
||||
var minCount: Int { get }
|
||||
var maxCount: Int { get }
|
||||
}
|
||||
|
||||
45
Sources/RelationshipProtocol.swift
Normal file
45
Sources/RelationshipProtocol.swift
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// RelationshipProtocol.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 Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - RelationshipProtocol
|
||||
|
||||
internal protocol RelationshipProtocol: class {
|
||||
|
||||
var keyPath: KeyPath { get }
|
||||
var isToMany: Bool { get }
|
||||
var isOrdered: Bool { get }
|
||||
var deleteRule: NSDeleteRule { get }
|
||||
var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) { get }
|
||||
var affectedByKeyPaths: () -> Set<String> { get }
|
||||
weak var parentObject: CoreStoreObject? { get set }
|
||||
var versionHashModifier: () -> String? { get }
|
||||
var renamingIdentifier: () -> String? { get }
|
||||
var minCount: Int { get }
|
||||
var maxCount: Int { get }
|
||||
}
|
||||
@@ -117,6 +117,20 @@ public final class SQLiteStore: LocalStorage {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Queries the file size (in bytes) of the store, or `nil` if the file does not exist yet
|
||||
*/
|
||||
public func fileSize() -> UInt64? {
|
||||
|
||||
guard let attribute = try? FileManager.default.attributesOfItem(atPath: self.fileURL.path),
|
||||
let sizeAttribute = attribute[.size],
|
||||
let fileSize = sizeAttribute as? NSNumber else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return fileSize.uint64Value
|
||||
}
|
||||
|
||||
|
||||
// MARK: StorageInterface
|
||||
|
||||
@@ -191,6 +205,22 @@ public final class SQLiteStore: LocalStorage {
|
||||
return storeOptions
|
||||
}
|
||||
|
||||
/**
|
||||
Called by the `DataStack` to perform checkpoint operations on the storage. For `SQLiteStore`, this converts the database's WAL journaling mode to DELETE to force a checkpoint.
|
||||
*/
|
||||
public func cs_finalizeStorageAndWait(soureModelHint: NSManagedObjectModel) throws {
|
||||
|
||||
_ = try withExtendedLifetime(NSPersistentStoreCoordinator(managedObjectModel: soureModelHint)) { (coordinator: NSPersistentStoreCoordinator) in
|
||||
|
||||
try coordinator.addPersistentStore(
|
||||
ofType: type(of: self).storeType,
|
||||
configurationName: self.configuration,
|
||||
at: fileURL,
|
||||
options: [NSSQLitePragmasOption: ["journal_mode": "DELETE"]]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Called by the `DataStack` to perform actual deletion of the store file from disk. Do not call directly! The `sourceModel` argument is a hint for the existing store's model version. For `SQLiteStore`, this converts the database's WAL journaling mode to DELETE before deleting the file.
|
||||
*/
|
||||
|
||||
@@ -401,13 +401,14 @@ internal extension Collection where Iterator.Element == SelectTerm {
|
||||
return entity.attributesByName[components[0]]
|
||||
|
||||
default:
|
||||
guard let relationship = entity.relationshipsByName[components[0]] else {
|
||||
|
||||
return nil
|
||||
guard let relationship = entity.relationshipsByName[components[0]],
|
||||
let destinationEntity = relationship.destinationEntity else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return attributeDescription(
|
||||
for: components.dropFirst().joined(separator: "."),
|
||||
in: relationship.entity
|
||||
in: destinationEntity
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -418,18 +419,7 @@ internal extension Collection where Iterator.Element == SelectTerm {
|
||||
switch term {
|
||||
|
||||
case ._attribute(let keyPath):
|
||||
let entityDescription = fetchRequest.entity!
|
||||
if let attributeDescription = attributeDescription(for: keyPath, in: entityDescription) {
|
||||
|
||||
propertiesToFetch.append(attributeDescription)
|
||||
}
|
||||
else {
|
||||
|
||||
CoreStore.log(
|
||||
.warning,
|
||||
message: "The key path \"\(keyPath)\" could not be resolved in entity \(cs_typeName(entityDescription.managedObjectClassName)) as an attribute and will be ignored by \(cs_typeName(owner)) query clause."
|
||||
)
|
||||
}
|
||||
propertiesToFetch.append(keyPath)
|
||||
|
||||
case ._aggregate(let function, let keyPath, let alias, let nativeType):
|
||||
let entityDescription = fetchRequest.entity!
|
||||
|
||||
@@ -141,6 +141,11 @@ public protocol LocalStorage: StorageInterface {
|
||||
*/
|
||||
func dictionary(forOptions options: LocalStorageOptions) -> [AnyHashable: Any]?
|
||||
|
||||
/**
|
||||
Called by the `DataStack` to perform checkpoint operations on the storage. (SQLite stores for example, can convert the database's WAL journaling mode to DELETE to force a checkpoint)
|
||||
*/
|
||||
func cs_finalizeStorageAndWait(soureModelHint: NSManagedObjectModel) throws
|
||||
|
||||
/**
|
||||
Called by the `DataStack` to perform actual deletion of the store file from disk. **Do not call directly!** The `sourceModel` argument is a hint for the existing store's model version. Implementers can use the `sourceModel` to perform necessary store operations. (SQLite stores for example, can convert WAL journaling mode to DELETE before deleting)
|
||||
*/
|
||||
|
||||
@@ -158,6 +158,10 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
self.isCommitted = true
|
||||
let result = self.context.saveSynchronously(waitForMerge: waitForMerge)
|
||||
self.result = result
|
||||
defer {
|
||||
|
||||
self.context.reset()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
597
Sources/Transformable.swift
Normal file
597
Sources/Transformable.swift
Normal file
@@ -0,0 +1,597 @@
|
||||
//
|
||||
// Transformable.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: - DynamicObject
|
||||
|
||||
public extension DynamicObject where Self: CoreStoreObject {
|
||||
|
||||
/**
|
||||
The containing type for transformable properties. `Transformable` properties support types that conforms to `NSCoding & NSCopying`.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
- Important: `Transformable` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties.
|
||||
*/
|
||||
public typealias Transformable = TransformableContainer<Self>
|
||||
}
|
||||
|
||||
|
||||
// MARK: - TransformableContainer
|
||||
|
||||
/**
|
||||
The containing type for transformable properties. Use the `DynamicObject.Transformable` typealias instead for shorter syntax.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
*/
|
||||
public enum TransformableContainer<O: CoreStoreObject> {
|
||||
|
||||
// MARK: - Required
|
||||
|
||||
/**
|
||||
The containing type for transformable properties. Any type that conforms to `NSCoding & NSCopying` are supported.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
- Important: `Transformable.Required` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties.
|
||||
*/
|
||||
public final class Required<V: NSCoding & NSCopying>: AttributeProtocol {
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let color = Transformable.Required<UIColor>(
|
||||
"color",
|
||||
initial: UIColor.clear,
|
||||
isTransient: true,
|
||||
customGetter: Animal.getColor(_:)
|
||||
)
|
||||
}
|
||||
|
||||
private static func getColor(_ partialObject: PartialObject<Animal>) -> UIColor {
|
||||
let cachedColor = partialObject.primitiveValue(for: { $0.color })
|
||||
if cachedColor != UIColor.clear {
|
||||
|
||||
return cachedColor
|
||||
}
|
||||
let color: UIColor
|
||||
switch partialObject.value(for: { $0.species }) {
|
||||
|
||||
case "Swift": color = UIColor.orange
|
||||
case "Bulbasaur": color = UIColor.green
|
||||
default: color = UIColor.black
|
||||
}
|
||||
partialObject.setPrimitiveValue(color, for: { $0.color })
|
||||
return color
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter initial: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified.
|
||||
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.primitiveValue(for:)` instead of `PartialObject<O>.value(for:)`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.setPrimitiveValue(_:for:)` instead of `PartialObject<O>.setValue(_:for:)`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public init(
|
||||
_ keyPath: KeyPath,
|
||||
initial: @autoclosure @escaping () -> V,
|
||||
isIndexed: Bool = false,
|
||||
isTransient: Bool = false,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = initial
|
||||
self.isIndexed = isIndexed
|
||||
self.isTransient = isTransient
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
self.affectedByKeyPaths = affectedByKeyPaths
|
||||
}
|
||||
|
||||
/**
|
||||
The property value.
|
||||
*/
|
||||
public var value: V {
|
||||
|
||||
get {
|
||||
|
||||
CoreStore.assert(
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
if let customGetter = self.customGetter {
|
||||
|
||||
return customGetter(PartialObject<O>(object.rawObject!))
|
||||
}
|
||||
return object.rawObject!.value(forKey: self.keyPath)! as! V
|
||||
}
|
||||
}
|
||||
set {
|
||||
|
||||
CoreStore.assert(
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
if let customSetter = self.customSetter {
|
||||
|
||||
return customSetter(PartialObject<O>(object.rawObject!), newValue)
|
||||
}
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKey: self.keyPath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: AttributeProtocol
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
|
||||
return .transformableAttributeType
|
||||
}
|
||||
|
||||
public let keyPath: KeyPath
|
||||
|
||||
internal let isOptional = false
|
||||
internal let isIndexed: Bool
|
||||
internal let isTransient: Bool
|
||||
internal let versionHashModifier: () -> String?
|
||||
internal let renamingIdentifier: () -> String?
|
||||
internal let defaultValue: () -> Any?
|
||||
internal let affectedByKeyPaths: () -> Set<String>
|
||||
internal weak var parentObject: CoreStoreObject?
|
||||
|
||||
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in
|
||||
|
||||
guard let customGetter = self.customGetter else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any) -> Any? in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.willAccessValue(forKey: keyPath)
|
||||
defer {
|
||||
|
||||
rawObject.didAccessValue(forKey: keyPath)
|
||||
}
|
||||
let value = customGetter(PartialObject<O>(rawObject))
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in
|
||||
|
||||
guard let customSetter = self.customSetter else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.willChangeValue(forKey: keyPath)
|
||||
defer {
|
||||
|
||||
rawObject.didChangeValue(forKey: keyPath)
|
||||
}
|
||||
customSetter(
|
||||
PartialObject<O>(rawObject),
|
||||
newValue as! V
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: ((_ partialObject: PartialObject<O>) -> V)?
|
||||
private let customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)?
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 3.1, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)")
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
`default`: @autoclosure @escaping () -> V,
|
||||
isIndexed: Bool = false,
|
||||
isTransient: Bool = false,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(
|
||||
keyPath,
|
||||
initial: `default`,
|
||||
isIndexed: isIndexed,
|
||||
isTransient: isTransient,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Optional
|
||||
|
||||
/**
|
||||
The containing type for optional transformable properties. Any type that conforms to `NSCoding & NSCopying` are supported.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
- Important: `Transformable.Optional` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties.
|
||||
*/
|
||||
public final class Optional<V: NSCoding & NSCopying>: AttributeProtocol {
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let color = Transformable.Optional<UIColor>(
|
||||
"color",
|
||||
isTransient: true,
|
||||
customGetter: Animal.getColor(_:)
|
||||
)
|
||||
}
|
||||
|
||||
private static func getColor(_ partialObject: PartialObject<Animal>) -> UIColor? {
|
||||
if let cachedColor = partialObject.primitiveValue(for: { $0.color }) {
|
||||
return cachedColor
|
||||
}
|
||||
let color: UIColor?
|
||||
switch partialObject.value(for: { $0.species }) {
|
||||
|
||||
case "Swift": color = UIColor.orange
|
||||
case "Bulbasaur": color = UIColor.green
|
||||
default: return nil
|
||||
}
|
||||
partialObject.setPrimitiveValue(color, for: { $0.color })
|
||||
return color
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter initial: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified.
|
||||
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.primitiveValue(for:)` instead of `PartialObject<O>.value(for:)`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.setPrimitiveValue(_:for:)` instead of `PartialObject<O>.setValue(_:for:)`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public init(
|
||||
_ keyPath: KeyPath,
|
||||
initial: @autoclosure @escaping () -> V? = nil,
|
||||
isIndexed: Bool = false,
|
||||
isTransient: Bool = false,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ partialObject: PartialObject<O>) -> V?)? = nil,
|
||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = initial
|
||||
self.isIndexed = isIndexed
|
||||
self.isTransient = isTransient
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
self.affectedByKeyPaths = affectedByKeyPaths
|
||||
}
|
||||
|
||||
/**
|
||||
The property value.
|
||||
*/
|
||||
public var value: V? {
|
||||
|
||||
get {
|
||||
|
||||
CoreStore.assert(
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
if let customGetter = self.customGetter {
|
||||
|
||||
return customGetter(PartialObject<O>(object.rawObject!))
|
||||
}
|
||||
return object.rawObject!.value(forKey: self.keyPath) as! V?
|
||||
}
|
||||
}
|
||||
set {
|
||||
|
||||
CoreStore.assert(
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
if let customSetter = self.customSetter {
|
||||
|
||||
return customSetter(PartialObject<O>(object.rawObject!), newValue)
|
||||
}
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKey: self.keyPath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: AttributeProtocol
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
|
||||
return .transformableAttributeType
|
||||
}
|
||||
|
||||
public let keyPath: KeyPath
|
||||
|
||||
internal let isOptional = true
|
||||
internal let isIndexed: Bool
|
||||
internal let isTransient: Bool
|
||||
internal let versionHashModifier: () -> String?
|
||||
internal let renamingIdentifier: () -> String?
|
||||
internal let defaultValue: () -> Any?
|
||||
internal let affectedByKeyPaths: () -> Set<String>
|
||||
internal weak var parentObject: CoreStoreObject?
|
||||
|
||||
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in
|
||||
|
||||
guard let customGetter = self.customGetter else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any) -> Any? in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.willAccessValue(forKey: keyPath)
|
||||
defer {
|
||||
|
||||
rawObject.didAccessValue(forKey: keyPath)
|
||||
}
|
||||
let value = customGetter(PartialObject<O>(rawObject))
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in
|
||||
|
||||
guard let customSetter = self.customSetter else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.willChangeValue(forKey: keyPath)
|
||||
defer {
|
||||
|
||||
rawObject.didChangeValue(forKey: keyPath)
|
||||
}
|
||||
customSetter(
|
||||
PartialObject<O>(rawObject),
|
||||
newValue as! V?
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: ((_ partialObject: PartialObject<O>) -> V?)?
|
||||
private let customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)?
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 3.1, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)")
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
`default`: @autoclosure @escaping () -> V?,
|
||||
isIndexed: Bool = false,
|
||||
isTransient: Bool = false,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ partialObject: PartialObject<O>) -> V?)? = nil,
|
||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(
|
||||
keyPath,
|
||||
initial: `default`,
|
||||
isIndexed: isIndexed,
|
||||
isTransient: isTransient,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Operations
|
||||
|
||||
infix operator .= : AssignmentPrecedence
|
||||
infix operator .== : ComparisonPrecedence
|
||||
|
||||
extension TransformableContainer.Required {
|
||||
|
||||
/**
|
||||
Assigns a transformable value to the property. The operation
|
||||
```
|
||||
animal.color .= UIColor.red
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.color.value = UIColor.red
|
||||
```
|
||||
*/
|
||||
public static func .= (_ property: TransformableContainer<O>.Required<V>, _ newValue: V) {
|
||||
|
||||
property.value = newValue
|
||||
}
|
||||
|
||||
/**
|
||||
Assigns a transformable value from another property. The operation
|
||||
```
|
||||
animal.nickname .= anotherAnimal.species
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.nickname.value = anotherAnimal.species.value
|
||||
```
|
||||
*/
|
||||
public static func .= <O2>(_ property: TransformableContainer<O>.Required<V>, _ property2: TransformableContainer<O2>.Required<V>) {
|
||||
|
||||
property.value = property2.value
|
||||
}
|
||||
}
|
||||
|
||||
extension TransformableContainer.Optional {
|
||||
|
||||
/**
|
||||
Assigns an optional transformable value to the property. The operation
|
||||
```
|
||||
animal.color .= UIColor.red
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.color.value = UIColor.red
|
||||
```
|
||||
*/
|
||||
public static func .= (_ property: TransformableContainer<O>.Optional<V>, _ newValue: V?) {
|
||||
|
||||
property.value = newValue
|
||||
}
|
||||
|
||||
/**
|
||||
Assigns an optional transformable value from another property. The operation
|
||||
```
|
||||
animal.color .= anotherAnimal.color
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.color.value = anotherAnimal.color.value
|
||||
```
|
||||
*/
|
||||
public static func .= <O2>(_ property: TransformableContainer<O>.Optional<V>, _ property2: TransformableContainer<O2>.Optional<V>) {
|
||||
|
||||
property.value = property2.value
|
||||
}
|
||||
|
||||
/**
|
||||
Assigns a transformable value from another property. The operation
|
||||
```
|
||||
animal.color .= anotherAnimal.color
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.color.value = anotherAnimal.color.value
|
||||
```
|
||||
*/
|
||||
public static func .= <O2>(_ property: TransformableContainer<O>.Optional<V>, _ property2: TransformableContainer<O2>.Required<V>) {
|
||||
|
||||
property.value = property2.value
|
||||
}
|
||||
}
|
||||
@@ -43,19 +43,6 @@ public extension DynamicObject where Self: CoreStoreObject {
|
||||
- Important: `Value` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties.
|
||||
*/
|
||||
public typealias Value = ValueContainer<Self>
|
||||
|
||||
/**
|
||||
The containing type for transformable properties. `Transformable` properties support types that conforms to `NSCoding & NSCopying`.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
- Important: `Transformable` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties.
|
||||
*/
|
||||
public typealias Transformable = TransformableContainer<Self>
|
||||
}
|
||||
|
||||
|
||||
@@ -92,39 +79,57 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
let title = Value.Required<String>("title", default: "Mr.")
|
||||
let name = Value.Required<String>(
|
||||
"name",
|
||||
customGetter: { (`self`, getValue) in
|
||||
return "\(self.title.value) \(getValue())"
|
||||
}
|
||||
let title = Value.Required<String>("title", initial: "Mr.")
|
||||
let name = Value.Required<String>("name")
|
||||
let displayName = Value.Required<String>(
|
||||
"displayName",
|
||||
isTransient: true,
|
||||
customGetter: Person.getName(_:)
|
||||
)
|
||||
|
||||
private static func getName(_ partialObject: PartialObject<Person>) -> String {
|
||||
let cachedDisplayName = partialObject.primitiveValue(for: { $0.displayName })
|
||||
if !cachedDisplayName.isEmpty {
|
||||
return cachedDisplayName
|
||||
}
|
||||
let title = partialObject.value(for: { $0.title })
|
||||
let name = partialObject.value(for: { $0.name })
|
||||
let displayName = "\(title) \(name)"
|
||||
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
|
||||
return displayName
|
||||
}
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter default: the initial value for the property when the object is first created. For types that implement `EmptyableAttributeType`s, this argument may be omitted and the type's "empty" value will be used instead (e.g. `false` for `Bool`, `0` for `Int`, `""` for `String`, etc.)
|
||||
- parameter initial: the initial value for the property when the object is first created
|
||||
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter customGetter: use this closure to make final transformations to the property's value before returning from the getter.
|
||||
- parameter self: the `CoreStoreObject`
|
||||
- parameter getValue: the original getter for the property
|
||||
- parameter customSetter: use this closure to make final transformations to the new value before assigning to the property.
|
||||
- parameter setValue: the original setter for the property
|
||||
- parameter finalNewValue: the transformed new value
|
||||
- parameter originalNewValue: the original new value
|
||||
- parameter customGetter: use this closure as an "override" for the default property getter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.primitiveValue(for:)` instead of `PartialObject<O>.value(for:)`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter customSetter: use this closure as an "override" for the default property setter. The closure receives a `PartialObject<O>`, which acts as a fast, type-safe KVC interface for `CoreStoreObject`. The reason a `CoreStoreObject` instance is not passed directly is because the Core Data runtime is not aware of `CoreStoreObject` properties' static typing, and so loading those info everytime KVO invokes this accessor method incurs a cumulative performance hit (especially in KVO-heavy operations such as `ListMonitor` observing.) When accessing the property value from `PartialObject<O>`, make sure to use `PartialObject<O>.setPrimitiveValue(_:for:)` instead of `PartialObject<O>.setValue(_:for:)`, which would unintentionally execute the same closure again recursively.
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public init(_ keyPath: KeyPath, `default`: V, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) }) {
|
||||
public init(
|
||||
_ keyPath: KeyPath,
|
||||
initial: @autoclosure @escaping () -> V,
|
||||
isIndexed: Bool = false,
|
||||
isTransient: Bool = false,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.isIndexed = isIndexed
|
||||
self.isTransient = isTransient
|
||||
self.defaultValue = `default`.cs_toImportableNativeType()
|
||||
self.defaultValue = { initial().cs_toImportableNativeType() }
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
self.affectedByKeyPaths = affectedByKeyPaths
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,45 +139,50 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return self.customGetter(
|
||||
object,
|
||||
{ () -> V in
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
if let customGetter = self.customGetter {
|
||||
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { V.cs_fromImportableNativeType($0 as! V.ImportableNativeType)! }
|
||||
)
|
||||
return customGetter(PartialObject<O>(object.rawObject!))
|
||||
}
|
||||
)
|
||||
return V.cs_fromImportableNativeType(
|
||||
object.rawObject!.value(forKey: self.keyPath)! as! V.ImportableNativeType
|
||||
)!
|
||||
}
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
self.customSetter(
|
||||
object,
|
||||
{ (newValue: V) -> Void in
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
if let customSetter = self.customSetter {
|
||||
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath,
|
||||
willSetValue: { $0.cs_toImportableNativeType() }
|
||||
)
|
||||
},
|
||||
newValue
|
||||
)
|
||||
return customSetter(PartialObject<O>(object.rawObject!), newValue)
|
||||
}
|
||||
return object.rawObject!.setValue(
|
||||
newValue.cs_toImportableNativeType(),
|
||||
forKey: self.keyPath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,20 +199,87 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
internal let isOptional = false
|
||||
internal let isIndexed: Bool
|
||||
internal let isTransient: Bool
|
||||
internal let defaultValue: Any?
|
||||
internal let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
internal let versionHashModifier: () -> String?
|
||||
internal let renamingIdentifier: () -> String?
|
||||
internal let defaultValue: () -> Any?
|
||||
internal let affectedByKeyPaths: () -> Set<String>
|
||||
internal weak var parentObject: CoreStoreObject?
|
||||
|
||||
internal var parentObject: () -> CoreStoreObject = {
|
||||
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in
|
||||
|
||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
||||
guard let customGetter = self.customGetter else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any) -> Any? in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.willAccessValue(forKey: keyPath)
|
||||
defer {
|
||||
|
||||
rawObject.didAccessValue(forKey: keyPath)
|
||||
}
|
||||
let value = customGetter(PartialObject<O>(rawObject))
|
||||
return value.cs_toImportableNativeType()
|
||||
}
|
||||
}
|
||||
|
||||
internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in
|
||||
|
||||
guard let customSetter = self.customSetter else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.willChangeValue(forKey: keyPath)
|
||||
defer {
|
||||
|
||||
rawObject.didChangeValue(forKey: keyPath)
|
||||
}
|
||||
customSetter(
|
||||
PartialObject<O>(rawObject),
|
||||
V.cs_fromImportableNativeType(newValue as! V.ImportableNativeType)!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
|
||||
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
|
||||
private let customGetter: ((_ partialObject: PartialObject<O>) -> V)?
|
||||
private let customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)?
|
||||
|
||||
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 3.1, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)")
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
`default`: @autoclosure @escaping () -> V,
|
||||
isIndexed: Bool = false,
|
||||
isTransient: Bool = false,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ partialObject: PartialObject<O>) -> V)? = nil,
|
||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.init(
|
||||
keyPath,
|
||||
initial: `default`,
|
||||
isIndexed: isIndexed,
|
||||
isTransient: isTransient,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -225,17 +302,28 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
let title = Value.Required<String>("title", default: "Mr.")
|
||||
let name = Value.Required<String>(
|
||||
"name",
|
||||
customGetter: { (`self`, getValue) in
|
||||
return "\(self.title.value) \(getValue())"
|
||||
}
|
||||
let title = Value.Optional<String>("title", initial: "Mr.")
|
||||
let name = Value.Optional<String>("name")
|
||||
let displayName = Value.Optional<String>(
|
||||
"displayName",
|
||||
isTransient: true,
|
||||
customGetter: Person.getName(_:)
|
||||
)
|
||||
|
||||
private static func getName(_ partialObject: PartialObject<Person>) -> String? {
|
||||
if let cachedDisplayName = partialObject.primitiveValue(for: { $0.displayName }) {
|
||||
return cachedDisplayName
|
||||
}
|
||||
let title = partialObject.value(for: { $0.title })
|
||||
let name = partialObject.value(for: { $0.name })
|
||||
let displayName = "\(title) \(name)"
|
||||
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
|
||||
return displayName
|
||||
}
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter default: the initial value for the property when the object is first created. Defaults to `nil` if not specified.
|
||||
- parameter initial: the initial value for the property when the object is first created. Defaults to `nil` if not specified.
|
||||
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
@@ -247,17 +335,28 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
- parameter setValue: the original setter for the property
|
||||
- parameter finalNewValue: the transformed new value
|
||||
- parameter originalNewValue: the original new value
|
||||
- parameter affectedByKeyPaths: a set of key paths for properties whose values affect the value of the receiver. This is similar to `NSManagedObject.keyPathsForValuesAffectingValue(forKey:)`.
|
||||
*/
|
||||
public init(_ keyPath: KeyPath, `default`: V? = nil, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V?) -> V? = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void = { $1($2) }) {
|
||||
public init(
|
||||
_ keyPath: KeyPath,
|
||||
initial: @autoclosure @escaping () -> V? = nil,
|
||||
isIndexed: Bool = false,
|
||||
isTransient: Bool = false,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ partialObject: PartialObject<O>) -> V?)? = nil,
|
||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.isIndexed = isIndexed
|
||||
self.isTransient = isTransient
|
||||
self.defaultValue = `default`?.cs_toImportableNativeType()
|
||||
self.defaultValue = { initial()?.cs_toImportableNativeType() }
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
self.affectedByKeyPaths = affectedByKeyPaths
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,45 +366,49 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return self.customGetter(
|
||||
object,
|
||||
{ () -> V? in
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
if let customGetter = self.customGetter {
|
||||
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { ($0 as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType) }
|
||||
)
|
||||
return customGetter(PartialObject<O>(object.rawObject!))
|
||||
}
|
||||
)
|
||||
return (object.rawObject!.value(forKey: self.keyPath) as! V.ImportableNativeType?)
|
||||
.flatMap(V.cs_fromImportableNativeType)
|
||||
}
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
self.parentObject != nil,
|
||||
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
self.customSetter(
|
||||
object,
|
||||
{ (newValue: V?) -> Void in
|
||||
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
if let customSetter = self.customSetter {
|
||||
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath,
|
||||
willSetValue: { $0?.cs_toImportableNativeType() }
|
||||
)
|
||||
},
|
||||
newValue
|
||||
)
|
||||
return customSetter(PartialObject<O>(object.rawObject!), newValue)
|
||||
}
|
||||
object.rawObject!.setValue(
|
||||
newValue?.cs_toImportableNativeType(),
|
||||
forKey: self.keyPath
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,324 +424,87 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
internal let isOptional = true
|
||||
internal let isIndexed: Bool
|
||||
internal let isTransient: Bool
|
||||
internal let defaultValue: Any?
|
||||
internal let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
internal let versionHashModifier: () -> String?
|
||||
internal let renamingIdentifier: () -> String?
|
||||
internal let defaultValue: () -> Any?
|
||||
internal let affectedByKeyPaths: () -> Set<String>
|
||||
internal weak var parentObject: CoreStoreObject?
|
||||
|
||||
internal var parentObject: () -> CoreStoreObject = {
|
||||
internal private(set) lazy var getter: CoreStoreManagedObject.CustomGetter? = cs_lazy { [unowned self] in
|
||||
|
||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
||||
guard let customGetter = self.customGetter else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any) -> Any? in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.willAccessValue(forKey: keyPath)
|
||||
defer {
|
||||
|
||||
rawObject.didAccessValue(forKey: keyPath)
|
||||
}
|
||||
let value = customGetter(PartialObject<O>(rawObject))
|
||||
return value?.cs_toImportableNativeType()
|
||||
}
|
||||
}
|
||||
|
||||
internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in
|
||||
|
||||
guard let customSetter = self.customSetter else {
|
||||
|
||||
return nil
|
||||
}
|
||||
let keyPath = self.keyPath
|
||||
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||
|
||||
let rawObject = id as! CoreStoreManagedObject
|
||||
rawObject.willChangeValue(forKey: keyPath)
|
||||
defer {
|
||||
|
||||
rawObject.didChangeValue(forKey: keyPath)
|
||||
}
|
||||
customSetter(
|
||||
PartialObject<O>(rawObject),
|
||||
(newValue as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
|
||||
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
|
||||
}
|
||||
}
|
||||
|
||||
public extension ValueContainer.Required where V: EmptyableAttributeType {
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property. This convenience initializer uses the `EmptyableAttributeType`'s "empty" value as the initial value for the property when the object is first created (e.g. `false` for `Bool`, `0` for `Int`, `""` for `String`, etc.)
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
let title = Value.Required<String>("title") // initial value defaults to empty string
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter customGetter: use this closure to make final transformations to the property's value before returning from the getter.
|
||||
- parameter self: the `CoreStoreObject`
|
||||
- parameter getValue: the original getter for the property
|
||||
- parameter customSetter: use this closure to make final transformations to the new value before assigning to the property.
|
||||
- parameter setValue: the original setter for the property
|
||||
- parameter finalNewValue: the transformed new value
|
||||
- parameter originalNewValue: the original new value
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) }) {
|
||||
private let customGetter: ((_ partialObject: PartialObject<O>) -> V?)?
|
||||
private let customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)?
|
||||
|
||||
self.init(
|
||||
keyPath,
|
||||
default: V.cs_emptyValue(),
|
||||
isIndexed: isIndexed,
|
||||
isTransient: isTransient,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - TransformableContainer
|
||||
|
||||
/**
|
||||
The containing type for transformable properties. Use the `DynamicObject.Transformable` typealias instead for shorter syntax.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
*/
|
||||
public enum TransformableContainer<O: CoreStoreObject> {
|
||||
|
||||
// MARK: - Required
|
||||
|
||||
/**
|
||||
The containing type for transformable properties. Any type that conforms to `NSCoding & NSCopying` are supported.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
- Important: `Transformable.Required` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties.
|
||||
*/
|
||||
public final class Required<V: NSCoding & NSCopying>: AttributeProtocol {
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter default: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified.
|
||||
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter customGetter: use this closure to make final transformations to the property's value before returning from the getter.
|
||||
- parameter self: the `CoreStoreObject`
|
||||
- parameter getValue: the original getter for the property
|
||||
- parameter customSetter: use this closure to make final transformations to the new value before assigning to the property.
|
||||
- parameter setValue: the original setter for the property
|
||||
- parameter finalNewValue: the transformed new value
|
||||
- parameter originalNewValue: the original new value
|
||||
*/
|
||||
public init(_ keyPath: KeyPath, `default`: V, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) }) {
|
||||
// MARK: Deprecated
|
||||
|
||||
@available(*, deprecated: 3.1, renamed: "init(_:initial:isIndexed:isTransient:versionHashModifier:renamingIdentifier:customGetter:customSetter:affectedByKeyPaths:)")
|
||||
public convenience init(
|
||||
_ keyPath: KeyPath,
|
||||
`default`: @autoclosure @escaping () -> V?,
|
||||
isIndexed: Bool = false,
|
||||
isTransient: Bool = false,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
renamingIdentifier: @autoclosure @escaping () -> String? = nil,
|
||||
customGetter: ((_ partialObject: PartialObject<O>) -> V?)? = nil,
|
||||
customSetter: ((_ partialObject: PartialObject<O>, _ newValue: V?) -> Void)? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = `default`
|
||||
self.isIndexed = isIndexed
|
||||
self.isTransient = isTransient
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
self.init(
|
||||
keyPath,
|
||||
initial: `default`,
|
||||
isIndexed: isIndexed,
|
||||
isTransient: isTransient,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter,
|
||||
affectedByKeyPaths: affectedByKeyPaths
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
The property value.
|
||||
*/
|
||||
public var value: V {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return self.customGetter(
|
||||
object,
|
||||
{ () -> V in
|
||||
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { $0 as! V }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
self.customSetter(
|
||||
object,
|
||||
{ (newValue: V) -> Void in
|
||||
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath
|
||||
)
|
||||
},
|
||||
newValue
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: AttributeProtocol
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
|
||||
return .transformableAttributeType
|
||||
}
|
||||
|
||||
public let keyPath: KeyPath
|
||||
|
||||
internal let isOptional = false
|
||||
internal let isIndexed: Bool
|
||||
internal let isTransient: Bool
|
||||
internal let defaultValue: Any?
|
||||
internal let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var parentObject: () -> CoreStoreObject = {
|
||||
|
||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
|
||||
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Optional
|
||||
|
||||
/**
|
||||
The containing type for optional transformable properties. Any type that conforms to `NSCoding & NSCopying` are supported.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let species = Value.Required<String>("species")
|
||||
let nickname = Value.Optional<String>("nickname")
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
- Important: `Transformable.Optional` properties are required to be stored properties. Computed properties will be ignored, including `lazy` and `weak` properties.
|
||||
*/
|
||||
public final class Optional<V: NSCoding & NSCopying>: AttributeProtocol {
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property.
|
||||
```
|
||||
class Animal: CoreStoreObject {
|
||||
let color = Transformable.Optional<UIColor>("color")
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter default: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified.
|
||||
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter customGetter: use this closure to make final transformations to the property's value before returning from the getter.
|
||||
- parameter self: the `CoreStoreObject`
|
||||
- parameter getValue: the original getter for the property
|
||||
- parameter customSetter: use this closure to make final transformations to the new value before assigning to the property.
|
||||
- parameter setValue: the original setter for the property
|
||||
- parameter finalNewValue: the transformed new value
|
||||
- parameter originalNewValue: the original new value
|
||||
*/
|
||||
public init(_ keyPath: KeyPath, `default`: V? = nil, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V?) -> V? = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void = { $1($2) }) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.defaultValue = `default`
|
||||
self.isIndexed = isIndexed
|
||||
self.isTransient = isTransient
|
||||
self.versionHashModifier = versionHashModifier
|
||||
self.renamingIdentifier = renamingIdentifier
|
||||
self.customGetter = customGetter
|
||||
self.customSetter = customSetter
|
||||
}
|
||||
|
||||
/**
|
||||
The property value.
|
||||
*/
|
||||
public var value: V? {
|
||||
|
||||
get {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
return self.customGetter(
|
||||
object,
|
||||
{ () -> V? in
|
||||
|
||||
return object.rawObject!.getValue(
|
||||
forKvcKey: self.keyPath,
|
||||
didGetValue: { $0 as! V? }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
set {
|
||||
|
||||
let object = self.parentObject() as! O
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
object.rawObject!.isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
self.customSetter(
|
||||
object,
|
||||
{ (newValue: V?) -> Void in
|
||||
|
||||
object.rawObject!.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath
|
||||
)
|
||||
},
|
||||
newValue
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: AttributeProtocol
|
||||
|
||||
internal static var attributeType: NSAttributeType {
|
||||
|
||||
return .transformableAttributeType
|
||||
}
|
||||
|
||||
public let keyPath: KeyPath
|
||||
|
||||
internal let isOptional = true
|
||||
internal let isIndexed: Bool
|
||||
internal let isTransient: Bool
|
||||
internal let defaultValue: Any?
|
||||
internal let versionHashModifier: String?
|
||||
internal let renamingIdentifier: String?
|
||||
|
||||
internal var parentObject: () -> CoreStoreObject = {
|
||||
|
||||
CoreStore.abort("Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types.")
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
|
||||
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
|
||||
}
|
||||
}
|
||||
|
||||
@@ -848,101 +714,3 @@ extension ValueContainer.Optional {
|
||||
return property.value == property2.value
|
||||
}
|
||||
}
|
||||
|
||||
extension TransformableContainer.Required {
|
||||
|
||||
/**
|
||||
Assigns a transformable value to the property. The operation
|
||||
```
|
||||
animal.color .= UIColor.red
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.color.value = UIColor.red
|
||||
```
|
||||
*/
|
||||
public static func .= (_ property: TransformableContainer<O>.Required<V>, _ newValue: V) {
|
||||
|
||||
property.value = newValue
|
||||
}
|
||||
|
||||
/**
|
||||
Assigns a transformable value from another property. The operation
|
||||
```
|
||||
animal.nickname .= anotherAnimal.species
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.nickname.value = anotherAnimal.species.value
|
||||
```
|
||||
*/
|
||||
public static func .= <O2: CoreStoreObject>(_ property: TransformableContainer<O>.Required<V>, _ property2: TransformableContainer<O2>.Required<V>) {
|
||||
|
||||
property.value = property2.value
|
||||
}
|
||||
}
|
||||
|
||||
extension TransformableContainer.Optional {
|
||||
|
||||
/**
|
||||
Assigns an optional transformable value to the property. The operation
|
||||
```
|
||||
animal.color .= UIColor.red
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.color.value = UIColor.red
|
||||
```
|
||||
*/
|
||||
public static func .= (_ property: TransformableContainer<O>.Optional<V>, _ newValue: V?) {
|
||||
|
||||
property.value = newValue
|
||||
}
|
||||
|
||||
/**
|
||||
Assigns an optional transformable value from another property. The operation
|
||||
```
|
||||
animal.color .= anotherAnimal.color
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.color.value = anotherAnimal.color.value
|
||||
```
|
||||
*/
|
||||
public static func .= <O2: CoreStoreObject>(_ property: TransformableContainer<O>.Optional<V>, _ property2: TransformableContainer<O2>.Optional<V>) {
|
||||
|
||||
property.value = property2.value
|
||||
}
|
||||
|
||||
/**
|
||||
Assigns a transformable value from another property. The operation
|
||||
```
|
||||
animal.color .= anotherAnimal.color
|
||||
```
|
||||
is equivalent to
|
||||
```
|
||||
animal.color.value = anotherAnimal.color.value
|
||||
```
|
||||
*/
|
||||
public static func .= <O2: CoreStoreObject>(_ property: TransformableContainer<O>.Optional<V>, _ property2: TransformableContainer<O2>.Required<V>) {
|
||||
|
||||
property.value = property2.value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - AttributeProtocol
|
||||
|
||||
internal protocol AttributeProtocol: class {
|
||||
|
||||
static var attributeType: NSAttributeType { get }
|
||||
|
||||
var keyPath: KeyPath { get }
|
||||
var isOptional: Bool { get }
|
||||
var isIndexed: Bool { get }
|
||||
var isTransient: Bool { get }
|
||||
var defaultValue: Any? { get }
|
||||
var versionHashModifier: String? { get }
|
||||
var renamingIdentifier: String? { get }
|
||||
var parentObject: () -> CoreStoreObject { get set }
|
||||
}
|
||||
|
||||
@@ -42,6 +42,36 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable {
|
||||
return Where(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate]))
|
||||
}
|
||||
|
||||
/**
|
||||
Combines two `Where` predicates together using `AND` operator.
|
||||
- parameter left: the left hand side `Where` clause
|
||||
- parameter right: the right hand side `Where` clause
|
||||
- returns: Return `left` unchanged if `right` is nil
|
||||
*/
|
||||
public static func && (left: Where, right: Where?) -> Where {
|
||||
|
||||
if let right = right {
|
||||
|
||||
return left && right
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
||||
/**
|
||||
Combines two `Where` predicates together using `AND` operator.
|
||||
- parameter left: the left hand side `Where` clause
|
||||
- parameter right: the right hand side `Where` clause
|
||||
- returns: Returns `right` unchanged if `left` is nil
|
||||
*/
|
||||
public static func && (left: Where?, right: Where) -> Where {
|
||||
|
||||
if let left = left {
|
||||
|
||||
return left && right
|
||||
}
|
||||
return right
|
||||
}
|
||||
|
||||
/**
|
||||
Combines two `Where` predicates together using `OR` operator
|
||||
*/
|
||||
@@ -50,6 +80,36 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable {
|
||||
return Where(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate]))
|
||||
}
|
||||
|
||||
/**
|
||||
Combines two `Where` predicates together using `OR` operator.
|
||||
- parameter left: the left hand side `Where` clause
|
||||
- parameter right: the right hand side `Where` clause
|
||||
- returns: Returns `left` unchanged if `right` is nil
|
||||
*/
|
||||
public static func || (left: Where, right: Where?) -> Where {
|
||||
|
||||
if let right = right {
|
||||
|
||||
return left || right
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
||||
/**
|
||||
Combines two `Where` predicates together using `OR` operator.
|
||||
- parameter left: the left hand side `Where` clause
|
||||
- parameter right: the right hand side `Where` clause
|
||||
- returns: Return `right` unchanged if `left` is nil
|
||||
*/
|
||||
public static func || (left: Where?, right: Where) -> Where {
|
||||
|
||||
if let left = left {
|
||||
|
||||
return left || right
|
||||
}
|
||||
return right
|
||||
}
|
||||
|
||||
/**
|
||||
Inverts the predicate of a `Where` clause using `NOT` operator
|
||||
*/
|
||||
@@ -216,3 +276,25 @@ public struct Where: FetchClause, QueryClause, DeleteClause, Hashable {
|
||||
return self.predicate.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Sequence where Element == Where
|
||||
|
||||
public extension Sequence where Iterator.Element == Where {
|
||||
|
||||
/**
|
||||
Combines multiple `Where` predicates together using `AND` operator
|
||||
*/
|
||||
public func combinedByAnd() -> Where {
|
||||
|
||||
return Where(NSCompoundPredicate(type: .and, subpredicates: self.map({ $0.predicate })))
|
||||
}
|
||||
|
||||
/**
|
||||
Combines multiple `Where` predicates together using `OR` operator
|
||||
*/
|
||||
public func combinedByOr() -> Where {
|
||||
|
||||
return Where(NSCompoundPredicate(type: .or, subpredicates: self.map({ $0.predicate })))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import Foundation
|
||||
/**
|
||||
A `SchemaMappingProvider` that tries to infer model migration between two `DynamicSchema` versions by loading an xcmappingmodel file from the specified `Bundle`. Throws `CoreStoreError.mappingModelNotFound` if the xcmappingmodel file cannot be found, or if the xcmappingmodel doesn't resolve the source and destination `DynamicSchema`.
|
||||
*/
|
||||
final class XcodeSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
public final class XcodeSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
|
||||
/**
|
||||
The source model version for the mapping.
|
||||
|
||||
Reference in New Issue
Block a user