mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-16 00:23:31 +01:00
Compare commits
28 Commits
temp/devel
...
4.0.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -1,6 +1,6 @@
|
|||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "CoreStore"
|
s.name = "CoreStore"
|
||||||
s.version = "4.0.1"
|
s.version = "4.0.5"
|
||||||
s.license = "MIT"
|
s.license = "MIT"
|
||||||
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
|
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
|
||||||
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
||||||
|
|||||||
@@ -238,6 +238,10 @@
|
|||||||
B538BA781D15B3E30003A766 /* CoreStoreBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = B538BA701D15B3E30003A766 /* CoreStoreBridge.m */; };
|
B538BA781D15B3E30003A766 /* CoreStoreBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = B538BA701D15B3E30003A766 /* CoreStoreBridge.m */; };
|
||||||
B538BA791D15B3E30003A766 /* 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 */; };
|
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 */; };
|
||||||
B53FB9FE1CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; };
|
B53FB9FE1CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; };
|
||||||
B53FBA001CAB2D2F00F0D40A /* 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 */; };
|
B53FBA011CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */; };
|
||||||
@@ -752,6 +756,7 @@
|
|||||||
B52FD3A91E3B3EF10001D919 /* NSManagedObject+Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+Logging.swift"; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
||||||
B53FB9FD1CAB2D2F00F0D40A /* CSMigrationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSMigrationResult.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>"; };
|
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>"; };
|
B53FBA0A1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CSCoreStore+Migrating.swift"; sourceTree = "<group>"; };
|
||||||
@@ -1425,16 +1430,17 @@
|
|||||||
children = (
|
children = (
|
||||||
B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */,
|
B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */,
|
||||||
B526613F1CADD585007B85D9 /* CoreStoreFetchRequest+CoreStore.swift */,
|
B526613F1CADD585007B85D9 /* CoreStoreFetchRequest+CoreStore.swift */,
|
||||||
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */,
|
B53B275E1EE3B92E00E9B352 /* CoreStoreManagedObject.swift */,
|
||||||
B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */,
|
B533C4DA1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift */,
|
||||||
B51260921E9B28F100402229 /* EntityIdentifier.swift */,
|
B51260921E9B28F100402229 /* EntityIdentifier.swift */,
|
||||||
B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */,
|
B54A6A541BA15F2A007870FD /* FetchedResultsControllerDelegate.swift */,
|
||||||
B5E834BA1B7691F3001D3D50 /* Functions.swift */,
|
B5E834BA1B7691F3001D3D50 /* Functions.swift */,
|
||||||
B5FAD6AB1B51285300714891 /* MigrationManager.swift */,
|
B5FAD6AB1B51285300714891 /* MigrationManager.swift */,
|
||||||
B5E84F2B1AFF849C0064E85B /* NotificationObserver.swift */,
|
B5E84F2B1AFF849C0064E85B /* NotificationObserver.swift */,
|
||||||
B533C4DA1D7D4BFA001383CB /* DispatchQueue+CoreStore.swift */,
|
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */,
|
||||||
B52FD3A91E3B3EF10001D919 /* NSManagedObject+Logging.swift */,
|
B56923C31EB823B4007C4DC9 /* NSEntityDescription+Migration.swift */,
|
||||||
B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */,
|
B58D0C621EAA0C7E003EDD87 /* NSManagedObject+DynamicModel.swift */,
|
||||||
|
B52FD3A91E3B3EF10001D919 /* NSManagedObject+Logging.swift */,
|
||||||
B5E84F2C1AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift */,
|
B5E84F2C1AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift */,
|
||||||
B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */,
|
B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */,
|
||||||
B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */,
|
B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */,
|
||||||
@@ -1875,6 +1881,7 @@
|
|||||||
B5E2222A1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */,
|
B5E2222A1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */,
|
||||||
B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */,
|
B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */,
|
||||||
B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
B56923E81EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||||
|
B53B275F1EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||||
B5D33A011E96012400C880DE /* Relationship.swift in Sources */,
|
B5D33A011E96012400C880DE /* Relationship.swift in Sources */,
|
||||||
B5E84EE81AFF84610064E85B /* CoreStoreLogger.swift in Sources */,
|
B5E84EE81AFF84610064E85B /* CoreStoreLogger.swift in Sources */,
|
||||||
B56923C91EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
B56923C91EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
||||||
@@ -2059,6 +2066,7 @@
|
|||||||
82BA18A71C4BBD2900A0916E /* CoreStore+Logging.swift in Sources */,
|
82BA18A71C4BBD2900A0916E /* CoreStore+Logging.swift in Sources */,
|
||||||
82BA18D81C4BBD7100A0916E /* WeakObject.swift in Sources */,
|
82BA18D81C4BBD7100A0916E /* WeakObject.swift in Sources */,
|
||||||
B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
B56923E91EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||||
|
B53B27601EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||||
B5D33A021E96012400C880DE /* Relationship.swift in Sources */,
|
B5D33A021E96012400C880DE /* Relationship.swift in Sources */,
|
||||||
B559CD4B1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */,
|
B559CD4B1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */,
|
||||||
B56923CA1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
B56923CA1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
||||||
@@ -2243,6 +2251,7 @@
|
|||||||
B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */,
|
B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */,
|
||||||
B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */,
|
||||||
B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
B56923EB1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||||
|
B53B27621EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||||
B5D33A041E96012400C880DE /* Relationship.swift in Sources */,
|
B5D33A041E96012400C880DE /* Relationship.swift in Sources */,
|
||||||
B52DD1C61BE1F94600949AFE /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
B52DD1C61BE1F94600949AFE /* NSManagedObjectContext+CoreStore.swift in Sources */,
|
||||||
B56923CC1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
B56923CC1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
||||||
@@ -2427,6 +2436,7 @@
|
|||||||
B563219F1BD65216006C9394 /* ObjectMonitor.swift in Sources */,
|
B563219F1BD65216006C9394 /* ObjectMonitor.swift in Sources */,
|
||||||
B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */,
|
B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */,
|
||||||
B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
B56923EA1EB827F5007C4DC9 /* InferredSchemaMappingProvider.swift in Sources */,
|
||||||
|
B53B27611EE3B92E00E9B352 /* CoreStoreManagedObject.swift in Sources */,
|
||||||
B5D33A031E96012400C880DE /* Relationship.swift in Sources */,
|
B5D33A031E96012400C880DE /* Relationship.swift in Sources */,
|
||||||
B559CD4C1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */,
|
B559CD4C1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */,
|
||||||
B56923CB1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
B56923CB1EB82410007C4DC9 /* NSManagedObjectModel+Migration.swift in Sources */,
|
||||||
|
|||||||
@@ -1,36 +1,25 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?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">
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12141" systemVersion="16F73" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||||
<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>
|
|
||||||
<entity name="Place" representedClassName="CoreStoreDemo.Place" syncable="YES">
|
<entity name="Place" representedClassName="CoreStoreDemo.Place" syncable="YES">
|
||||||
<attribute name="latitude" 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" 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="subtitle" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
|
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<entity name="TimeZone" representedClassName="CoreStoreDemo.TimeZone" syncable="YES">
|
<entity name="TimeZone" representedClassName="CoreStoreDemo.TimeZone" syncable="YES">
|
||||||
<attribute name="abbreviation" optional="YES" attributeType="String" 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="daylightSavingTimeOffset" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO" syncable="YES"/>
|
||||||
<attribute name="hasDaylightSavingTime" optional="YES" attributeType="Boolean" syncable="YES"/>
|
<attribute name="hasDaylightSavingTime" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
|
||||||
<attribute name="name" optional="YES" attributeType="String" 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>
|
</entity>
|
||||||
<configuration name="FetchingAndQueryingDemo">
|
<configuration name="FetchingAndQueryingDemo">
|
||||||
<memberEntity name="TimeZone"/>
|
<memberEntity name="TimeZone"/>
|
||||||
</configuration>
|
</configuration>
|
||||||
<configuration name="ObservingDemo">
|
|
||||||
<memberEntity name="Palette"/>
|
|
||||||
</configuration>
|
|
||||||
<configuration name="TransactionsDemo">
|
<configuration name="TransactionsDemo">
|
||||||
<memberEntity name="Place"/>
|
<memberEntity name="Place"/>
|
||||||
</configuration>
|
</configuration>
|
||||||
<elements>
|
<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="Place" positionX="261" positionY="225" width="128" height="105"/>
|
||||||
<element name="TimeZone" positionX="297" positionY="270" width="128" height="120"/>
|
<element name="TimeZone" positionX="297" positionY="270" width="128" height="120"/>
|
||||||
</elements>
|
</elements>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.0.1</string>
|
<string>4.0.3</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import UIKit
|
|||||||
import CoreStore
|
import CoreStore
|
||||||
|
|
||||||
|
|
||||||
private struct Static {
|
struct ColorsDemo {
|
||||||
|
|
||||||
enum Filter: String {
|
enum Filter: String {
|
||||||
|
|
||||||
@@ -33,8 +33,8 @@ private struct Static {
|
|||||||
switch self {
|
switch self {
|
||||||
|
|
||||||
case .all: return Where(true)
|
case .all: return Where(true)
|
||||||
case .light: return Where("%K >= %@", #keyPath(Palette.brightness), 0.9)
|
case .light: return Palette.where({ $0.brightness >= 0.9 })
|
||||||
case .dark: return Where("%K <= %@", #keyPath(Palette.brightness), 0.4)
|
case .dark: return Palette.where({ $0.brightness <= 0.4 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,25 +45,38 @@ private struct Static {
|
|||||||
|
|
||||||
self.palettes.refetch(
|
self.palettes.refetch(
|
||||||
self.filter.whereClause(),
|
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> = {
|
static let palettes: ListMonitor<Palette> = {
|
||||||
|
|
||||||
try! CoreStore.addStorageAndWait(
|
try! ColorsDemo.stack.addStorageAndWait(
|
||||||
SQLiteStore(
|
SQLiteStore(
|
||||||
fileName: "ColorsDemo.sqlite",
|
fileName: "ColorsDemo.sqlite",
|
||||||
configuration: "ObservingDemo",
|
|
||||||
localStorageOptions: .recreateStoreOnModelMismatch
|
localStorageOptions: .recreateStoreOnModelMismatch
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
return ColorsDemo.stack.monitorSectionedList(
|
||||||
return CoreStore.monitorSectionedList(
|
|
||||||
From<Palette>(),
|
From<Palette>(),
|
||||||
SectionBy(#keyPath(Palette.colorName)),
|
SectionBy(Palette.keyPath({ $0.colorName })),
|
||||||
OrderBy(.ascending(#keyPath(Palette.hue)))
|
Palette.orderBy(ascending: { $0.hue })
|
||||||
)
|
)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
@@ -77,7 +90,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
|
||||||
Static.palettes.removeObserver(self)
|
ColorsDemo.palettes.removeObserver(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -98,7 +111,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
]
|
]
|
||||||
|
|
||||||
let filterBarButton = UIBarButtonItem(
|
let filterBarButton = UIBarButtonItem(
|
||||||
title: Static.filter.rawValue,
|
title: ColorsDemo.filter.rawValue,
|
||||||
style: .plain,
|
style: .plain,
|
||||||
target: self,
|
target: self,
|
||||||
action: #selector(self.filterBarButtonItemTouched(_:))
|
action: #selector(self.filterBarButtonItemTouched(_:))
|
||||||
@@ -113,9 +126,9 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
]
|
]
|
||||||
self.filterBarButton = filterBarButton
|
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?) {
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||||
@@ -137,19 +150,19 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
|
|
||||||
return Static.palettes.numberOfSections()
|
return ColorsDemo.palettes.numberOfSections()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
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 {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "PaletteTableViewCell") as! PaletteTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: "PaletteTableViewCell") as! PaletteTableViewCell
|
||||||
|
|
||||||
let palette = Static.palettes[indexPath]
|
let palette = ColorsDemo.palettes[indexPath]
|
||||||
cell.colorView?.backgroundColor = palette.color
|
cell.colorView?.backgroundColor = palette.color
|
||||||
cell.label?.text = palette.colorText
|
cell.label?.text = palette.colorText
|
||||||
|
|
||||||
@@ -165,7 +178,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
|
|
||||||
self.performSegue(
|
self.performSegue(
|
||||||
withIdentifier: "ObjectObserverDemoViewController",
|
withIdentifier: "ObjectObserverDemoViewController",
|
||||||
sender: Static.palettes[indexPath]
|
sender: ColorsDemo.palettes[indexPath]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,8 +187,8 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
switch editingStyle {
|
switch editingStyle {
|
||||||
|
|
||||||
case .delete:
|
case .delete:
|
||||||
let palette = Static.palettes[indexPath]
|
let palette = ColorsDemo.palettes[indexPath]
|
||||||
CoreStore.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { (transaction) in
|
asynchronous: { (transaction) in
|
||||||
|
|
||||||
transaction.delete(palette)
|
transaction.delete(palette)
|
||||||
@@ -190,7 +203,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
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>) {
|
func listMonitorDidRefetch(_ monitor: ListMonitor<Palette>) {
|
||||||
|
|
||||||
self.filterBarButton?.title = Static.filter.rawValue
|
self.filterBarButton?.title = ColorsDemo.filter.rawValue
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
self.setTable(enabled: true)
|
self.setTable(enabled: true)
|
||||||
}
|
}
|
||||||
@@ -235,7 +248,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
|
|
||||||
if let cell = self.tableView.cellForRow(at: indexPath) as? PaletteTableViewCell {
|
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.colorView?.backgroundColor = palette.color
|
||||||
cell.label?.text = palette.colorText
|
cell.label?.text = palette.colorText
|
||||||
}
|
}
|
||||||
@@ -268,7 +281,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
|
|
||||||
@IBAction private dynamic func resetBarButtonItemTouched(_ sender: AnyObject?) {
|
@IBAction private dynamic func resetBarButtonItemTouched(_ sender: AnyObject?) {
|
||||||
|
|
||||||
CoreStore.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { (transaction) in
|
asynchronous: { (transaction) in
|
||||||
|
|
||||||
transaction.deleteAll(From<Palette>())
|
transaction.deleteAll(From<Palette>())
|
||||||
@@ -279,16 +292,16 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
|
|||||||
|
|
||||||
@IBAction private dynamic func filterBarButtonItemTouched(_ sender: AnyObject?) {
|
@IBAction private dynamic func filterBarButtonItemTouched(_ sender: AnyObject?) {
|
||||||
|
|
||||||
Static.filter = Static.filter.next()
|
ColorsDemo.filter = ColorsDemo.filter.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private dynamic func addBarButtonItemTouched(_ sender: AnyObject?) {
|
@IBAction private dynamic func addBarButtonItemTouched(_ sender: AnyObject?) {
|
||||||
|
|
||||||
CoreStore.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { (transaction) in
|
asynchronous: { (transaction) in
|
||||||
|
|
||||||
let palette = transaction.create(Into<Palette>())
|
let palette = transaction.create(Into<Palette>())
|
||||||
palette.setInitialValues()
|
palette.setInitialValues(in: transaction)
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
if let palette = newValue {
|
if let palette = newValue {
|
||||||
|
|
||||||
self.monitor = CoreStore.monitorObject(palette)
|
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
@@ -50,22 +50,22 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
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 {
|
else {
|
||||||
|
|
||||||
_ = try? CoreStore.perform(
|
_ = try? ColorsDemo.stack.perform(
|
||||||
synchronous: { (transaction) in
|
synchronous: { (transaction) in
|
||||||
|
|
||||||
let palette = transaction.create(Into(Palette.self))
|
let palette = transaction.create(Into<Palette>())
|
||||||
palette.setInitialValues()
|
palette.setInitialValues(in: transaction)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
let palette = CoreStore.fetchOne(From<Palette>(), OrderBy(.ascending(#keyPath(Palette.hue))))!
|
let palette = ColorsDemo.stack.fetchOne(From<Palette>(), Palette.orderBy(ascending: { $0.hue }))!
|
||||||
self.monitor = CoreStore.monitorObject(palette)
|
self.monitor = ColorsDemo.stack.monitorObject(palette)
|
||||||
}
|
}
|
||||||
|
|
||||||
super.init(coder: aDecoder)
|
super.init(coder: aDecoder)
|
||||||
@@ -121,12 +121,12 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
@IBAction dynamic func hueSliderValueDidChange(_ sender: AnyObject?) {
|
@IBAction dynamic func hueSliderValueDidChange(_ sender: AnyObject?) {
|
||||||
|
|
||||||
let hue = self.hueSlider?.value ?? 0
|
let hue = self.hueSlider?.value ?? 0
|
||||||
CoreStore.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { [weak self] (transaction) in
|
asynchronous: { [weak self] (transaction) in
|
||||||
|
|
||||||
if let palette = transaction.edit(self?.monitor?.object) {
|
if let palette = transaction.edit(self?.monitor?.object) {
|
||||||
|
|
||||||
palette.hue = Int32(hue)
|
palette.hue .= Int(hue)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
@@ -136,12 +136,12 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
@IBAction dynamic func saturationSliderValueDidChange(_ sender: AnyObject?) {
|
@IBAction dynamic func saturationSliderValueDidChange(_ sender: AnyObject?) {
|
||||||
|
|
||||||
let saturation = self.saturationSlider?.value ?? 0
|
let saturation = self.saturationSlider?.value ?? 0
|
||||||
CoreStore.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { [weak self] (transaction) in
|
asynchronous: { [weak self] (transaction) in
|
||||||
|
|
||||||
if let palette = transaction.edit(self?.monitor?.object) {
|
if let palette = transaction.edit(self?.monitor?.object) {
|
||||||
|
|
||||||
palette.saturation = saturation
|
palette.saturation .= saturation
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
@@ -151,12 +151,12 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
@IBAction dynamic func brightnessSliderValueDidChange(_ sender: AnyObject?) {
|
@IBAction dynamic func brightnessSliderValueDidChange(_ sender: AnyObject?) {
|
||||||
|
|
||||||
let brightness = self.brightnessSlider?.value ?? 0
|
let brightness = self.brightnessSlider?.value ?? 0
|
||||||
CoreStore.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { [weak self] (transaction) in
|
asynchronous: { [weak self] (transaction) in
|
||||||
|
|
||||||
if let palette = transaction.edit(self?.monitor?.object) {
|
if let palette = transaction.edit(self?.monitor?.object) {
|
||||||
|
|
||||||
palette.brightness = brightness
|
palette.brightness .= brightness
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
completion: { _ in }
|
completion: { _ in }
|
||||||
@@ -165,7 +165,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
@IBAction dynamic func deleteBarButtonTapped(_ sender: AnyObject?) {
|
@IBAction dynamic func deleteBarButtonTapped(_ sender: AnyObject?) {
|
||||||
|
|
||||||
CoreStore.perform(
|
ColorsDemo.stack.perform(
|
||||||
asynchronous: { [weak self] (transaction) in
|
asynchronous: { [weak self] (transaction) in
|
||||||
|
|
||||||
transaction.delete(self?.monitor?.object)
|
transaction.delete(self?.monitor?.object)
|
||||||
@@ -176,7 +176,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
func reloadPaletteInfo(_ palette: Palette, changedKeys: Set<String>?) {
|
func reloadPaletteInfo(_ palette: Palette, changedKeys: Set<String>?) {
|
||||||
|
|
||||||
self.colorNameLabel?.text = palette.colorName
|
self.colorNameLabel?.text = palette.colorName.value
|
||||||
|
|
||||||
let color = palette.color
|
let color = palette.color
|
||||||
self.colorNameLabel?.textColor = color
|
self.colorNameLabel?.textColor = color
|
||||||
@@ -184,17 +184,17 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
|
|||||||
|
|
||||||
self.hsbLabel?.text = palette.colorText
|
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
|
// MARK: - Palette
|
||||||
|
|
||||||
class Palette: NSManagedObject {
|
final class Palette: CoreStoreObject {
|
||||||
|
|
||||||
@NSManaged var hue: Int32
|
|
||||||
@NSManaged var saturation: Float
|
|
||||||
@NSManaged var brightness: Float
|
|
||||||
|
|
||||||
@objc dynamic var colorName: String {
|
let hue = Value.Required<Int>("hue")
|
||||||
|
let saturation = Value.Required<Float>("saturation")
|
||||||
|
let brightness = Value.Required<Float>("brightness")
|
||||||
|
|
||||||
|
let colorName = Value.Optional<String>(
|
||||||
|
"colorName",
|
||||||
|
isTransient: true,
|
||||||
|
customGetter: Palette.getCachedColorName
|
||||||
|
)
|
||||||
|
|
||||||
|
private static func getCachedColorName(_ instance: Palette, _ getValue: () -> String?) -> String? {
|
||||||
|
|
||||||
get {
|
if let colorName = getValue() {
|
||||||
|
|
||||||
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
|
return colorName
|
||||||
}
|
}
|
||||||
set {
|
|
||||||
|
let colorName: String
|
||||||
|
switch instance.hue.value % 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"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance.colorName.primitiveValue = colorName
|
||||||
|
return colorName
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Palette {
|
||||||
|
|
||||||
var color: UIColor {
|
var color: UIColor {
|
||||||
|
|
||||||
return UIColor(
|
return UIColor(
|
||||||
hue: CGFloat(self.hue) / 360.0,
|
hue: CGFloat(self.hue.value) / 360.0,
|
||||||
saturation: CGFloat(self.saturation),
|
saturation: CGFloat(self.saturation.value),
|
||||||
brightness: CGFloat(self.brightness),
|
brightness: CGFloat(self.brightness.value),
|
||||||
alpha: 1.0
|
alpha: 1.0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var colorText: String {
|
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.hue .= Int(arc4random_uniform(360))
|
||||||
self.saturation = 1.0
|
self.saturation .= Float(1.0)
|
||||||
self.brightness = Float(arc4random_uniform(70) + 30) / 100.0
|
self.brightness .= Float(arc4random_uniform(70) + 30) / 100.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger {
|
|||||||
case 2?:
|
case 2?:
|
||||||
DispatchQueue.global(qos: .background).async {
|
DispatchQueue.global(qos: .background).async {
|
||||||
|
|
||||||
_ = self.dataStack.fetchOne(From<Palette>())
|
_ = self.dataStack.fetchOne(From<Place>())
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
|
|||||||
self.present(alert, animated: true, completion: nil)
|
self.present(alert, animated: true, completion: nil)
|
||||||
|
|
||||||
|
|
||||||
let modelMetadata = withExtendedLifetime(DataStack(modelName: "MigrationDemo")) {
|
let modelMetadata = withExtendedLifetime(DataStack(xcodeModelName: "MigrationDemo")) {
|
||||||
(dataStack: DataStack) -> ModelMetadata in
|
(dataStack: DataStack) -> ModelMetadata in
|
||||||
|
|
||||||
let models = self.models
|
let models = self.models
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ private struct Static {
|
|||||||
|
|
||||||
static let facebookStack: DataStack = {
|
static let facebookStack: DataStack = {
|
||||||
|
|
||||||
let dataStack = DataStack(modelName: "StackSetupDemo")
|
let dataStack = DataStack(xcodeModelName: "StackSetupDemo")
|
||||||
try! dataStack.addStorageAndWait(
|
try! dataStack.addStorageAndWait(
|
||||||
SQLiteStore(
|
SQLiteStore(
|
||||||
fileName: "AccountsDemo_FB_Male.sqlite",
|
fileName: "AccountsDemo_FB_Male.sqlite",
|
||||||
@@ -55,7 +55,7 @@ private struct Static {
|
|||||||
|
|
||||||
static let twitterStack: DataStack = {
|
static let twitterStack: DataStack = {
|
||||||
|
|
||||||
let dataStack = DataStack(modelName: "StackSetupDemo")
|
let dataStack = DataStack(xcodeModelName: "StackSetupDemo")
|
||||||
try! dataStack.addStorageAndWait(
|
try! dataStack.addStorageAndWait(
|
||||||
SQLiteStore(
|
SQLiteStore(
|
||||||
fileName: "AccountsDemo_TW_Male.sqlite",
|
fileName: "AccountsDemo_TW_Male.sqlite",
|
||||||
|
|||||||
@@ -51,15 +51,49 @@ class Dog: Animal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Person: CoreStoreObject {
|
class Person: CoreStoreObject {
|
||||||
let title = Value.Required<String>("title", default: "Mr.")
|
let title = Value.Required<String>(
|
||||||
let name = Value.Required<String>(
|
"title",
|
||||||
"name",
|
default: "Mr.",
|
||||||
customGetter: { (`self`, getValue) in
|
customSetter: { (`self`, setValue, originalNewValue) in
|
||||||
|
|
||||||
return "\(self.title.value) \(getValue())"
|
setValue(originalNewValue)
|
||||||
|
self.displayName .= nil
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
let name = Value.Required<String>(
|
||||||
|
"name",
|
||||||
|
customSetter: { (`self`, setValue, originalNewValue) in
|
||||||
|
|
||||||
|
setValue(originalNewValue)
|
||||||
|
self.displayName .= nil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
let displayName = Value.Optional<String>(
|
||||||
|
"displayName",
|
||||||
|
isTransient: true,
|
||||||
|
customGetter: Person.cachedDisplayName(_:_:),
|
||||||
|
affectedByKeyPaths: Person.keyPathsAffectingDisplayName()
|
||||||
|
)
|
||||||
let pets = Relationship.ToManyUnordered<Animal>("pets", inverse: { $0.master })
|
let pets = Relationship.ToManyUnordered<Animal>("pets", inverse: { $0.master })
|
||||||
|
|
||||||
|
static func cachedDisplayName(_ instance: Person, _ getValue: () -> String?) -> String? {
|
||||||
|
|
||||||
|
if let cached = getValue() {
|
||||||
|
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
let primitiveValue = "\(instance.title.value) \(instance.name.value)"
|
||||||
|
instance.displayName .= primitiveValue
|
||||||
|
return primitiveValue
|
||||||
|
}
|
||||||
|
|
||||||
|
static func keyPathsAffectingDisplayName() -> Set<String> {
|
||||||
|
|
||||||
|
return [
|
||||||
|
self.keyPath({ $0.title }),
|
||||||
|
self.keyPath({ $0.name })
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -124,11 +158,25 @@ class DynamicModelTests: BaseTestDataTestCase {
|
|||||||
let person = transaction.create(Into<Person>())
|
let person = transaction.create(Into<Person>())
|
||||||
XCTAssertTrue(person.pets.value.isEmpty)
|
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"
|
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"
|
person.title .= "Sir"
|
||||||
XCTAssertEqual(person.name.value, "Sir John")
|
XCTAssertEqual(person.displayName.value, "Sir John")
|
||||||
|
|
||||||
person.pets.value.insert(dog)
|
person.pets.value.insert(dog)
|
||||||
XCTAssertEqual(person.pets.count, 1)
|
XCTAssertEqual(person.pets.count, 1)
|
||||||
|
|||||||
50
README.md
50
README.md
@@ -19,6 +19,7 @@ Unleashing the real power of Core Data with the elegance and safety of Swift
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
* **Swift 3.1:** iOS 8+ / macOS 10.10+ / watchOS 2.0+ / tvOS 9.0+
|
* **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).
|
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
|
### 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:
|
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:
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import CoreData
|
|||||||
- SeeAlso: `AsynchronousDataTransaction`
|
- SeeAlso: `AsynchronousDataTransaction`
|
||||||
*/
|
*/
|
||||||
@objc
|
@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.
|
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 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) {
|
public required init(_ swiftValue: AsynchronousDataTransaction) {
|
||||||
@@ -149,9 +149,9 @@ public final class CSAsynchronousDataTransaction: CSBaseDataTransaction {
|
|||||||
super.init(swiftValue as BaseDataTransaction)
|
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
|
@objc
|
||||||
public func fetchExistingObject(_ object: NSManagedObject) -> Any? {
|
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
|
@objc
|
||||||
public func fetchExistingObjectWithID(_ objectID: NSManagedObjectID) -> Any? {
|
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
|
@objc
|
||||||
public func fetchExistingObjects(_ objects: [NSManagedObject]) -> [Any] {
|
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
|
@objc
|
||||||
public func fetchExistingObjectsWithIDs(_ objectIDs: [NSManagedObjectID]) -> [Any] {
|
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? {
|
public func fetchOneFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> Any? {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||||
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
|
"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]? {
|
public func fetchAllFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> [Any]? {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||||
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
|
"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? {
|
public func fetchCountFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> NSNumber? {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||||
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
|
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
|
||||||
)
|
)
|
||||||
return self.bridgeToSwift.context
|
return self.swiftTransaction.context
|
||||||
.fetchCount(from, fetchClauses)
|
.fetchCount(from, fetchClauses)
|
||||||
.flatMap { NSNumber(value: $0) }
|
.flatMap { NSNumber(value: $0) }
|
||||||
}
|
}
|
||||||
@@ -143,10 +143,10 @@ public extension CSBaseDataTransaction {
|
|||||||
public func fetchObjectIDFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> NSManagedObjectID? {
|
public func fetchObjectIDFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> NSManagedObjectID? {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||||
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
|
"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? {
|
public func queryValueFrom(_ from: CSFrom, selectClause: CSSelect, queryClauses: [CSQueryClause]) -> Any? {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||||
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
|
"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]]? {
|
public func queryAttributesFrom(_ from: CSFrom, selectClause: CSSelect, queryClauses: [CSQueryClause]) -> [[String: Any]]? {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
self.bridgeToSwift.isRunningInAllowedQueue(),
|
self.swiftTransaction.isRunningInAllowedQueue(),
|
||||||
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
|
"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`
|
- SeeAlso: `BaseDataTransaction`
|
||||||
*/
|
*/
|
||||||
@objc
|
@objc
|
||||||
public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
public class CSBaseDataTransaction: NSObject {
|
||||||
|
|
||||||
// MARK: Object management
|
// MARK: Object management
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
@objc
|
@objc
|
||||||
public var hasChanges: Bool {
|
public var hasChanges: Bool {
|
||||||
|
|
||||||
return self.bridgeToSwift.hasChanges
|
return self.swiftTransaction.hasChanges
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +57,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
@objc
|
@objc
|
||||||
public func createInto(_ into: CSInto) -> Any {
|
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
|
@objc
|
||||||
public func editObject(_ object: NSManagedObject?) -> Any? {
|
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
|
@objc
|
||||||
public func editInto(_ into: CSInto, objectID: NSManagedObjectID) -> Any? {
|
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
|
@objc
|
||||||
public func deleteObject(_ object: NSManagedObject?) {
|
public func deleteObject(_ object: NSManagedObject?) {
|
||||||
|
|
||||||
self.bridgeToSwift.delete(object)
|
self.swiftTransaction.delete(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,7 +104,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
@objc
|
@objc
|
||||||
public func deleteObjects(_ objects: [NSManagedObject]) {
|
public func deleteObjects(_ objects: [NSManagedObject]) {
|
||||||
|
|
||||||
self.bridgeToSwift.delete(objects)
|
self.swiftTransaction.delete(objects)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,7 +113,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
@objc
|
@objc
|
||||||
public func refreshAndMergeAllObjects() {
|
public func refreshAndMergeAllObjects() {
|
||||||
|
|
||||||
self.bridgeToSwift.refreshAndMergeAllObjects()
|
self.swiftTransaction.refreshAndMergeAllObjects()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
@objc
|
@objc
|
||||||
public func insertedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> {
|
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
|
@objc
|
||||||
public func insertedObjectIDs() -> Set<NSManagedObjectID> {
|
public func insertedObjectIDs() -> Set<NSManagedObjectID> {
|
||||||
|
|
||||||
return self.bridgeToSwift.insertedObjectIDs()
|
return self.swiftTransaction.insertedObjectIDs()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,7 +151,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
@objc
|
@objc
|
||||||
public func insertedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> {
|
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
|
@objc
|
||||||
public func updatedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> {
|
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
|
@objc
|
||||||
public func updatedObjectIDs() -> Set<NSManagedObjectID> {
|
public func updatedObjectIDs() -> Set<NSManagedObjectID> {
|
||||||
|
|
||||||
return self.bridgeToSwift.updatedObjectIDs()
|
return self.swiftTransaction.updatedObjectIDs()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,7 +186,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
@objc
|
@objc
|
||||||
public func updatedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> {
|
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
|
@objc
|
||||||
public func deletedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> {
|
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
|
@objc
|
||||||
public func deletedObjectIDs() -> Set<NSManagedObjectID> {
|
public func deletedObjectIDs() -> Set<NSManagedObjectID> {
|
||||||
|
|
||||||
return self.bridgeToSwift.deletedObjectIDs()
|
return self.swiftTransaction.deletedObjectIDs()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -221,7 +221,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
@objc
|
@objc
|
||||||
public func deletedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> {
|
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 {
|
public override var hash: Int {
|
||||||
|
|
||||||
return ObjectIdentifier(self.bridgeToSwift).hashValue
|
return ObjectIdentifier(self.swiftTransaction).hashValue
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func isEqual(_ object: Any?) -> Bool {
|
public override func isEqual(_ object: Any?) -> Bool {
|
||||||
@@ -238,28 +238,20 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
|
|
||||||
return false
|
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
|
self.swiftTransaction = swiftValue
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
public var bridgeToSwift: BaseDataTransaction {
|
|
||||||
|
|
||||||
return self.swiftTransaction
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
private let swiftTransaction: BaseDataTransaction
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Deprecated
|
// MARK: Deprecated
|
||||||
|
|
||||||
@@ -267,20 +259,20 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
|
|||||||
@objc
|
@objc
|
||||||
public func insertedObjects() -> Set<NSManagedObject> {
|
public func insertedObjects() -> Set<NSManagedObject> {
|
||||||
|
|
||||||
return self.bridgeToSwift.insertedObjects()
|
return self.swiftTransaction.insertedObjects()
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated, message: "Use -[updatedObjectsOfType:] and pass the specific entity class")
|
@available(*, deprecated, message: "Use -[updatedObjectsOfType:] and pass the specific entity class")
|
||||||
@objc
|
@objc
|
||||||
public func updatedObjects() -> Set<NSManagedObject> {
|
public func updatedObjects() -> Set<NSManagedObject> {
|
||||||
|
|
||||||
return self.bridgeToSwift.updatedObjects()
|
return self.swiftTransaction.updatedObjects()
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated, message: "Use -[deletedObjectsOfType:] and pass the specific entity class")
|
@available(*, deprecated, message: "Use -[deletedObjectsOfType:] and pass the specific entity class")
|
||||||
@objc
|
@objc
|
||||||
public func deletedObjects() -> Set<NSManagedObject> {
|
public func deletedObjects() -> Set<NSManagedObject> {
|
||||||
|
|
||||||
return self.bridgeToSwift.deletedObjects()
|
return self.swiftTransaction.deletedObjects()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import CoreData
|
|||||||
- SeeAlso: `SynchronousDataTransaction`
|
- SeeAlso: `SynchronousDataTransaction`
|
||||||
*/
|
*/
|
||||||
@objc
|
@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.
|
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 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) {
|
public required init(_ swiftValue: SynchronousDataTransaction) {
|
||||||
@@ -139,9 +139,9 @@ public final class CSSynchronousDataTransaction: CSBaseDataTransaction {
|
|||||||
super.init(swiftValue as BaseDataTransaction)
|
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`
|
- SeeAlso: `UnsafeDataTransaction`
|
||||||
*/
|
*/
|
||||||
@objc
|
@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.
|
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 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) {
|
public required init(_ swiftValue: UnsafeDataTransaction) {
|
||||||
@@ -199,9 +199,9 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
|
|||||||
super.init(swiftValue as BaseDataTransaction)
|
super.init(swiftValue as BaseDataTransaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(_ swiftValue: BaseDataTransaction) {
|
public required override init(_ swiftValue: BaseDataTransaction) {
|
||||||
|
|
||||||
super.init(swiftValue as! UnsafeDataTransaction)
|
super.init(swiftValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll
|
|||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
@nonobjc
|
@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(
|
self.init(
|
||||||
context: dataStack.mainContext,
|
context: dataStack.mainContext,
|
||||||
@@ -47,33 +47,18 @@ internal final class CoreStoreFetchedResultsController: NSFetchedResultsControll
|
|||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc
|
@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,
|
fetchRequest,
|
||||||
context: context,
|
context: context,
|
||||||
applyAffectedStores: false
|
applyAffectedStores: false
|
||||||
)
|
)
|
||||||
applyFetchClauses(fetchRequest)
|
applyFetchClauses(fetchRequest)
|
||||||
|
|
||||||
if let from = from {
|
self.reapplyAffectedStores = { fetchRequest, context in
|
||||||
|
|
||||||
self.reapplyAffectedStores = { fetchRequest, context in
|
return from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
super.init(
|
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
|
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.
|
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.
|
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))
|
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.
|
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 {
|
public static func descending<O: CoreStoreObject, V: ImportableAttributeType>(_ attribute: (Self) -> ValueContainer<O>.Optional<V>) -> OrderBy {
|
||||||
|
|
||||||
return OrderBy(.descending(attribute(self.meta).keyPath))
|
return OrderBy(.descending(attribute(self.meta).keyPath))
|
||||||
@@ -163,6 +233,18 @@ public extension ValueContainer.Required {
|
|||||||
|
|
||||||
return Where("%K >= %@", attribute.keyPath, value)
|
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)
|
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) {
|
public required init(rawObject: NSManagedObject) {
|
||||||
|
|
||||||
self.isMeta = false
|
self.isMeta = false
|
||||||
self.rawObject = rawObject
|
self.rawObject = (rawObject as! CoreStoreManagedObject)
|
||||||
self.initializeAttributes(Mirror(reflecting: self), { [unowned self] in self })
|
self.initializeAttributes(Mirror(reflecting: self), self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,13 +110,13 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable {
|
|||||||
|
|
||||||
// MARK: Internal
|
// MARK: Internal
|
||||||
|
|
||||||
internal let rawObject: NSManagedObject?
|
internal let rawObject: CoreStoreManagedObject?
|
||||||
internal let isMeta: Bool
|
internal let isMeta: Bool
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private func initializeAttributes(_ mirror: Mirror, _ parentObject: @escaping () -> CoreStoreObject) {
|
private func initializeAttributes(_ mirror: Mirror, _ parentObject: CoreStoreObject) {
|
||||||
|
|
||||||
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, parentObject) })
|
_ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, parentObject) })
|
||||||
for child in mirror.children {
|
for child in mirror.children {
|
||||||
|
|||||||
@@ -200,35 +200,44 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
|
|
||||||
public func rawModel() -> NSManagedObjectModel {
|
public func rawModel() -> NSManagedObjectModel {
|
||||||
|
|
||||||
if let cachedRawModel = self.cachedRawModel {
|
return CoreStoreSchema.barrierQueue.sync(flags: .barrier) {
|
||||||
|
|
||||||
return cachedRawModel
|
if let cachedRawModel = self.cachedRawModel {
|
||||||
}
|
|
||||||
let rawModel = NSManagedObjectModel()
|
return cachedRawModel
|
||||||
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
}
|
||||||
for entity in self.allEntities {
|
let rawModel = NSManagedObjectModel()
|
||||||
|
var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||||
let entityDescription = self.entityDescription(
|
var allCustomGettersSetters: [DynamicEntity: [KeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||||
for: entity,
|
for entity in self.allEntities {
|
||||||
initializer: CoreStoreSchema.firstPassCreateEntityDescription
|
|
||||||
|
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(
|
rawModel.entities = entityDescriptionsByEntity.values.sorted(by: { $0.name! < $1.name! })
|
||||||
entities
|
for (configuration, entities) in self.entitiesByConfiguration {
|
||||||
.map({ entityDescriptionsByEntity[$0]! })
|
|
||||||
.sorted(by: { $0.name! < $1.name! }),
|
rawModel.setEntities(
|
||||||
forConfigurationName: configuration
|
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 let allEntities: Set<DynamicEntity>
|
||||||
|
|
||||||
private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
private var entityDescriptionsByEntity: [DynamicEntity: NSEntityDescription] = [:]
|
||||||
|
private var customGettersSettersByEntity: [DynamicEntity: [KeyPath: CoreStoreManagedObject.CustomGetterSetter]] = [:]
|
||||||
private weak var cachedRawModel: NSManagedObjectModel?
|
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] {
|
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
|
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()
|
let entityDescription = NSEntityDescription()
|
||||||
entityDescription.coreStoreEntity = entity
|
entityDescription.coreStoreEntity = entity
|
||||||
entityDescription.name = entity.entityName
|
entityDescription.name = entity.entityName
|
||||||
entityDescription.isAbstract = entity.isAbstract
|
entityDescription.isAbstract = entity.isAbstract
|
||||||
entityDescription.versionHashModifier = entity.versionHashModifier
|
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] {
|
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
|
||||||
|
|
||||||
var propertyDescriptions: [NSPropertyDescription] = []
|
var propertyDescriptions: [NSPropertyDescription] = []
|
||||||
@@ -279,11 +293,13 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
description.attributeType = type(of: attribute).attributeType
|
description.attributeType = type(of: attribute).attributeType
|
||||||
description.isOptional = attribute.isOptional
|
description.isOptional = attribute.isOptional
|
||||||
description.isIndexed = attribute.isIndexed
|
description.isIndexed = attribute.isIndexed
|
||||||
description.defaultValue = attribute.defaultValue
|
description.defaultValue = attribute.defaultValue()
|
||||||
description.isTransient = attribute.isTransient
|
description.isTransient = attribute.isTransient
|
||||||
description.versionHashModifier = attribute.versionHashModifier
|
description.versionHashModifier = attribute.versionHashModifier
|
||||||
description.renamingIdentifier = attribute.renamingIdentifier
|
description.renamingIdentifier = attribute.renamingIdentifier
|
||||||
propertyDescriptions.append(description)
|
propertyDescriptions.append(description)
|
||||||
|
keyPathsByAffectedKeyPaths[attribute.keyPath] = attribute.affectedByKeyPaths()
|
||||||
|
customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter)
|
||||||
|
|
||||||
case let relationship as RelationshipProtocol:
|
case let relationship as RelationshipProtocol:
|
||||||
let description = NSRelationshipDescription()
|
let description = NSRelationshipDescription()
|
||||||
@@ -295,6 +311,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
description.versionHashModifier = relationship.versionHashModifier
|
description.versionHashModifier = relationship.versionHashModifier
|
||||||
description.renamingIdentifier = relationship.renamingIdentifier
|
description.renamingIdentifier = relationship.renamingIdentifier
|
||||||
propertyDescriptions.append(description)
|
propertyDescriptions.append(description)
|
||||||
|
keyPathsByAffectedKeyPaths[relationship.keyPath] = relationship.affectedByKeyPaths()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
@@ -302,9 +319,9 @@ public final class CoreStoreSchema: DynamicSchema {
|
|||||||
}
|
}
|
||||||
return propertyDescriptions
|
return propertyDescriptions
|
||||||
}
|
}
|
||||||
|
|
||||||
entityDescription.properties = createProperties(for: entity.type as! CoreStoreObject.Type)
|
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]) {
|
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.
|
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.
|
The source model version for the mapping.
|
||||||
@@ -352,7 +352,7 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
|||||||
)
|
)
|
||||||
func expression(forSource sourceEntity: NSEntityDescription) -> NSExpression {
|
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
|
let sourceEntitiesByName = sourceModel.entitiesByName
|
||||||
@@ -427,22 +427,25 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
|||||||
let destinationAttribute = destination.attribute
|
let destinationAttribute = destination.attribute
|
||||||
let propertyMapping = NSPropertyMapping()
|
let propertyMapping = NSPropertyMapping()
|
||||||
propertyMapping.name = destinationAttribute.name
|
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)
|
attributeMappings.append(propertyMapping)
|
||||||
}
|
}
|
||||||
return attributeMappings
|
return attributeMappings
|
||||||
}
|
}
|
||||||
let entityMappingName = entityMapping.name!
|
|
||||||
entityMapping.relationshipMappings = autoreleasepool { () -> [NSPropertyMapping] in
|
entityMapping.relationshipMappings = autoreleasepool { () -> [NSPropertyMapping] in
|
||||||
|
|
||||||
|
let sourceRelationships = sourceEntity.cs_resolvedRelationshipRenamingIdentities()
|
||||||
let destinationRelationships = destinationEntity.cs_resolvedRelationshipRenamingIdentities()
|
let destinationRelationships = destinationEntity.cs_resolvedRelationshipRenamingIdentities()
|
||||||
var relationshipMappings: [NSPropertyMapping] = []
|
var relationshipMappings: [NSPropertyMapping] = []
|
||||||
for (_, destination) in destinationRelationships {
|
for (renamingIdentifier, destination) in destinationRelationships {
|
||||||
|
|
||||||
|
let sourceRelationship = sourceRelationships[renamingIdentifier]!.relationship
|
||||||
let destinationRelationship = destination.relationship
|
let destinationRelationship = destination.relationship
|
||||||
|
let sourceRelationshipName = sourceRelationship.name
|
||||||
|
|
||||||
let propertyMapping = NSPropertyMapping()
|
let propertyMapping = NSPropertyMapping()
|
||||||
propertyMapping.name = destinationRelationship.name
|
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)
|
relationshipMappings.append(propertyMapping)
|
||||||
}
|
}
|
||||||
return relationshipMappings
|
return relationshipMappings
|
||||||
@@ -483,17 +486,24 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
|||||||
}
|
}
|
||||||
userInfo[CustomEntityMigrationPolicy.UserInfoKey.sourceAttributesByDestinationKey] = sourceAttributesByDestinationKey
|
userInfo[CustomEntityMigrationPolicy.UserInfoKey.sourceAttributesByDestinationKey] = sourceAttributesByDestinationKey
|
||||||
}
|
}
|
||||||
let entityMappingName = entityMapping.name!
|
|
||||||
entityMapping.relationshipMappings = autoreleasepool { () -> [NSPropertyMapping] in
|
entityMapping.relationshipMappings = autoreleasepool { () -> [NSPropertyMapping] in
|
||||||
|
|
||||||
|
let sourceRelationships = sourceEntity.cs_resolvedRelationshipRenamingIdentities()
|
||||||
let destinationRelationships = destinationEntity.cs_resolvedRelationshipRenamingIdentities()
|
let destinationRelationships = destinationEntity.cs_resolvedRelationshipRenamingIdentities()
|
||||||
|
let transformedRenamingIdentifiers = Set(destinationRelationships.keys)
|
||||||
|
.intersection(sourceRelationships.keys)
|
||||||
|
|
||||||
var relationshipMappings: [NSPropertyMapping] = []
|
var relationshipMappings: [NSPropertyMapping] = []
|
||||||
for (_, destination) in destinationRelationships {
|
for renamingIdentifier in transformedRenamingIdentifiers {
|
||||||
|
|
||||||
let destinationRelationship = destination.relationship
|
let sourceRelationship = sourceRelationships[renamingIdentifier]!.relationship
|
||||||
|
let destinationRelationship = destinationRelationships[renamingIdentifier]!.relationship
|
||||||
|
let sourceRelationshipName = sourceRelationship.name
|
||||||
|
let destinationRelationshipName = destinationRelationship.name
|
||||||
|
|
||||||
let propertyMapping = NSPropertyMapping()
|
let propertyMapping = NSPropertyMapping()
|
||||||
propertyMapping.name = destinationRelationship.name
|
propertyMapping.name = destinationRelationshipName
|
||||||
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)
|
relationshipMappings.append(propertyMapping)
|
||||||
}
|
}
|
||||||
return relationshipMappings
|
return relationshipMappings
|
||||||
@@ -545,11 +555,7 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
|||||||
)
|
)
|
||||||
if let dInstance = destinationObject?.rawObject {
|
if let dInstance = destinationObject?.rawObject {
|
||||||
|
|
||||||
manager.associate(
|
manager.associate(sourceInstance: sInstance, withDestinationInstance: dInstance, for: mapping)
|
||||||
sourceInstance: sInstance,
|
|
||||||
withDestinationInstance: dInstance,
|
|
||||||
for: mapping
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,77 +676,76 @@ open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
|||||||
allMappedSourceKeys[sourceEntity] = destinationEntity
|
allMappedSourceKeys[sourceEntity] = destinationEntity
|
||||||
allMappedDestinationKeys[destinationEntity] = sourceEntity
|
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
|
case (nil, nil):
|
||||||
let destinationEntity = destinationRenamingIdentifiers[renamingIdentifier]!.entity
|
if sourceEntity.versionHash == destinationEntity.versionHash {
|
||||||
let sourceEntityName = sourceEntity.name!
|
|
||||||
let destinationEntityName = destinationEntity.name!
|
|
||||||
switch (allMappedSourceKeys[sourceEntityName], allMappedDestinationKeys[destinationEntityName]) {
|
|
||||||
|
|
||||||
case (nil, nil):
|
copyMappings.insert(
|
||||||
if sourceEntity.versionHash == destinationEntity.versionHash {
|
.copyEntity(
|
||||||
|
sourceEntity: sourceEntityName,
|
||||||
copyMappings.insert(
|
destinationEntity: destinationEntityName
|
||||||
.copyEntity(
|
|
||||||
sourceEntity: sourceEntityName,
|
|
||||||
destinationEntity: destinationEntityName
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
else {
|
}
|
||||||
|
else {
|
||||||
transformMappings.insert(
|
|
||||||
.transformEntity(
|
transformMappings.insert(
|
||||||
sourceEntity: sourceEntityName,
|
.transformEntity(
|
||||||
destinationEntity: destinationEntityName,
|
sourceEntity: sourceEntityName,
|
||||||
transformer: CustomMapping.inferredTransformation
|
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
|
|
||||||
}
|
}
|
||||||
}
|
allMappedSourceKeys[sourceEntityName] = destinationEntityName
|
||||||
for renamingIdentifier in removedRenamingIdentifiers {
|
allMappedDestinationKeys[destinationEntityName] = sourceEntityName
|
||||||
|
|
||||||
let sourceEntity = sourceRenamingIdentifiers[renamingIdentifier]!.entity
|
case (""?, nil):
|
||||||
let sourceEntityName = sourceEntity.name!
|
insertMappings.insert(.insertEntity(destinationEntity: destinationEntityName))
|
||||||
switch allMappedSourceKeys[sourceEntityName] {
|
allMappedDestinationKeys[destinationEntityName] = ""
|
||||||
|
|
||||||
case nil:
|
|
||||||
deleteMappings.insert(.deleteEntity(sourceEntity: sourceEntityName))
|
|
||||||
allMappedSourceKeys[sourceEntityName] = ""
|
|
||||||
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for renamingIdentifier in addedRenamingIdentifiers {
|
|
||||||
|
|
||||||
let destinationEntity = destinationRenamingIdentifiers[renamingIdentifier]!.entity
|
case (nil, ""?):
|
||||||
let destinationEntityName = destinationEntity.name!
|
deleteMappings.insert(.deleteEntity(sourceEntity: sourceEntityName))
|
||||||
switch allMappedDestinationKeys[destinationEntityName] {
|
allMappedSourceKeys[sourceEntityName] = ""
|
||||||
|
|
||||||
case nil:
|
default:
|
||||||
insertMappings.insert(.insertEntity(destinationEntity: destinationEntityName))
|
continue
|
||||||
allMappedDestinationKeys[destinationEntityName] = ""
|
}
|
||||||
|
}
|
||||||
default:
|
for renamingIdentifier in removedRenamingIdentifiers {
|
||||||
continue
|
|
||||||
}
|
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)
|
return (deleteMappings, insertMappings, copyMappings, transformMappings)
|
||||||
|
|||||||
@@ -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`.
|
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
|
// MARK: Equatable
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>FMWK</string>
|
<string>FMWK</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.0.0</string>
|
<string>4.0.5</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
|
|||||||
@@ -704,13 +704,13 @@ public final class ListMonitor<D: DynamicObject>: Hashable {
|
|||||||
|
|
||||||
guard let `self` = self,
|
guard let `self` = self,
|
||||||
let userInfo = note.userInfo,
|
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
|
return
|
||||||
}
|
}
|
||||||
callback(
|
callback(
|
||||||
self,
|
self,
|
||||||
object,
|
ObjectType.cs_fromRaw(object: rawObject),
|
||||||
userInfo[String(describing: IndexPath.self)] as? IndexPath,
|
userInfo[String(describing: IndexPath.self)] as? IndexPath,
|
||||||
userInfo["\(String(describing: IndexPath.self)).New"] as? IndexPath
|
userInfo["\(String(describing: IndexPath.self)).New"] as? IndexPath
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -54,17 +54,65 @@ internal extension NSEntityDescription {
|
|||||||
|
|
||||||
if let newValue = newValue {
|
if let newValue = newValue {
|
||||||
|
|
||||||
var userInfo: [AnyHashable : Any] = [
|
cs_setUserInfo { (userInfo) in
|
||||||
UserInfoKey.CoreStoreManagedObjectTypeName: NSStringFromClass(newValue.type),
|
|
||||||
UserInfoKey.CoreStoreManagedObjectEntityName: newValue.entityName,
|
userInfo[UserInfoKey.CoreStoreManagedObjectTypeName] = NSStringFromClass(newValue.type)
|
||||||
UserInfoKey.CoreStoreManagedObjectIsAbstract: newValue.isAbstract
|
userInfo[UserInfoKey.CoreStoreManagedObjectEntityName] = newValue.entityName
|
||||||
]
|
userInfo[UserInfoKey.CoreStoreManagedObjectIsAbstract] = newValue.isAbstract
|
||||||
userInfo[UserInfoKey.CoreStoreManagedObjectVersionHashModifier] = newValue.versionHashModifier
|
userInfo[UserInfoKey.CoreStoreManagedObjectVersionHashModifier] = newValue.versionHashModifier
|
||||||
self.userInfo = userInfo
|
}
|
||||||
}
|
}
|
||||||
else {
|
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
|
// MARK: - UserInfoKey
|
||||||
|
|
||||||
fileprivate enum UserInfoKey {
|
private enum UserInfoKey {
|
||||||
|
|
||||||
fileprivate static let CoreStoreManagedObjectTypeName = "CoreStoreManagedObjectTypeName"
|
fileprivate static let CoreStoreManagedObjectTypeName = "CoreStoreManagedObjectTypeName"
|
||||||
fileprivate static let CoreStoreManagedObjectEntityName = "CoreStoreManagedObjectEntityName"
|
fileprivate static let CoreStoreManagedObjectEntityName = "CoreStoreManagedObjectEntityName"
|
||||||
fileprivate static let CoreStoreManagedObjectIsAbstract = "CoreStoreManagedObjectIsAbstract"
|
fileprivate static let CoreStoreManagedObjectIsAbstract = "CoreStoreManagedObjectIsAbstract"
|
||||||
fileprivate static let CoreStoreManagedObjectVersionHashModifier = "CoreStoreManagedObjectVersionHashModifier"
|
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
|
// MARK: - Private
|
||||||
|
|
||||||
@available(OSX 10.12, *)
|
@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(
|
let controller = CoreStoreFetchedResultsController(
|
||||||
context: context,
|
context: context,
|
||||||
|
|||||||
@@ -68,12 +68,12 @@ public extension CSUnsafeDataTransaction {
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
@available(OSX 10.12, *)
|
@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(
|
let controller = CoreStoreFetchedResultsController(
|
||||||
context: context,
|
context: context,
|
||||||
fetchRequest: CoreStoreFetchRequest().dynamicCast(),
|
fetchRequest: CoreStoreFetchRequest().dynamicCast(),
|
||||||
from: from?.bridgeToSwift,
|
from: from.bridgeToSwift,
|
||||||
sectionBy: sectionBy?.bridgeToSwift,
|
sectionBy: sectionBy?.bridgeToSwift,
|
||||||
applyFetchClauses: { (fetchRequest) in
|
applyFetchClauses: { (fetchRequest) in
|
||||||
|
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ public final class ObjectMonitor<D: DynamicObject>: Equatable {
|
|||||||
let fetchedResultsController = CoreStoreFetchedResultsController(
|
let fetchedResultsController = CoreStoreFetchedResultsController(
|
||||||
context: context,
|
context: context,
|
||||||
fetchRequest: fetchRequest.dynamicCast(),
|
fetchRequest: fetchRequest.dynamicCast(),
|
||||||
from: nil as From<ObjectType>?,
|
from: From<ObjectType>([objectID.persistentStore?.configurationName]),
|
||||||
applyFetchClauses: Where("SELF", isEqualTo: objectID).applyToFetchRequest
|
applyFetchClauses: Where("SELF", isEqualTo: objectID).applyToFetchRequest
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,54 +244,64 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
|||||||
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
internal weak 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 var nativeValue: NSManagedObject? {
|
internal var nativeValue: NSManagedObject? {
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
|
||||||
return object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { $0 as! NSManagedObject? }
|
|
||||||
)
|
)
|
||||||
|
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 {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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."
|
|
||||||
)
|
|
||||||
object.rawObject!.setValue(
|
|
||||||
newValue,
|
|
||||||
forKvcKey: self.keyPath
|
|
||||||
)
|
)
|
||||||
|
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
|
// 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: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set<String>) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
self.deleteRule = deleteRule.nativeValue
|
self.deleteRule = deleteRule.nativeValue
|
||||||
self.inverse = (D.self, inverseKeyPath)
|
self.inverse = (D.self, inverseKeyPath)
|
||||||
self.versionHashModifier = versionHashModifier
|
self.versionHashModifier = versionHashModifier
|
||||||
self.renamingIdentifier = renamingIdentifier
|
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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -377,48 +513,57 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
|||||||
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
internal weak 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 var nativeValue: NSOrderedSet {
|
internal var nativeValue: NSOrderedSet {
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
|
||||||
return object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { ($0 as! NSOrderedSet?) ?? [] }
|
|
||||||
)
|
)
|
||||||
|
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 {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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."
|
|
||||||
)
|
|
||||||
object.rawObject!.setValue(
|
|
||||||
newValue,
|
|
||||||
forKvcKey: self.keyPath
|
|
||||||
)
|
)
|
||||||
|
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
|
// 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: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set<String>) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
self.deleteRule = deleteRule.nativeValue
|
self.deleteRule = deleteRule.nativeValue
|
||||||
@@ -429,6 +574,7 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
|||||||
let range = (Swift.max(0, minCount) ... maxCount)
|
let range = (Swift.max(0, minCount) ... maxCount)
|
||||||
self.minCount = range.lowerBound
|
self.minCount = range.lowerBound
|
||||||
self.maxCount = range.upperBound
|
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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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 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 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 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: String? = nil,
|
||||||
|
renamingIdentifier: 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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -570,48 +787,57 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
|||||||
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: String?
|
internal let renamingIdentifier: String?
|
||||||
|
internal let affectedByKeyPaths: () -> Set<String>
|
||||||
internal var parentObject: () -> CoreStoreObject = {
|
internal weak 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 var nativeValue: NSSet {
|
internal var nativeValue: NSSet {
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"Attempted to access values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
|
||||||
return object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { ($0 as! NSSet?) ?? [] }
|
|
||||||
)
|
)
|
||||||
|
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 {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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."
|
|
||||||
)
|
|
||||||
object.rawObject!.setValue(
|
|
||||||
newValue,
|
|
||||||
forKvcKey: self.keyPath
|
|
||||||
)
|
)
|
||||||
|
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
|
// 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: String?, renamingIdentifier: String?, affectedByKeyPaths: @autoclosure @escaping () -> Set<String>) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
self.deleteRule = deleteRule.nativeValue
|
self.deleteRule = deleteRule.nativeValue
|
||||||
@@ -622,6 +848,7 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
|||||||
let range = (Swift.max(0, minCount) ... maxCount)
|
let range = (Swift.max(0, minCount) ... maxCount)
|
||||||
self.minCount = range.lowerBound
|
self.minCount = range.lowerBound
|
||||||
self.maxCount = range.upperBound
|
self.maxCount = range.upperBound
|
||||||
|
self.affectedByKeyPaths = affectedByKeyPaths
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -658,7 +885,7 @@ extension RelationshipContainer.ToManyOrdered: RandomAccessCollection {
|
|||||||
public func makeIterator() -> Iterator {
|
public func makeIterator() -> Iterator {
|
||||||
|
|
||||||
let iterator = self.nativeValue.makeIterator()
|
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 {
|
public func makeIterator() -> Iterator {
|
||||||
|
|
||||||
let iterator = self.nativeValue.makeIterator()
|
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) }) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,7 +1215,8 @@ internal protocol RelationshipProtocol: class {
|
|||||||
var isOrdered: Bool { get }
|
var isOrdered: Bool { get }
|
||||||
var deleteRule: NSDeleteRule { get }
|
var deleteRule: NSDeleteRule { get }
|
||||||
var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) { get }
|
var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) { get }
|
||||||
var parentObject: () -> CoreStoreObject { get set }
|
var affectedByKeyPaths: () -> Set<String> { get }
|
||||||
|
weak var parentObject: CoreStoreObject? { get set }
|
||||||
var versionHashModifier: String? { get }
|
var versionHashModifier: String? { get }
|
||||||
var renamingIdentifier: String? { get }
|
var renamingIdentifier: String? { get }
|
||||||
var minCount: Int { get }
|
var minCount: Int { get }
|
||||||
|
|||||||
@@ -114,17 +114,28 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
- parameter setValue: the original setter for the property
|
- parameter setValue: the original setter for the property
|
||||||
- parameter finalNewValue: the transformed new value
|
- parameter finalNewValue: the transformed new value
|
||||||
- parameter originalNewValue: the original 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, 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,
|
||||||
|
`default`: @autoclosure @escaping () -> V,
|
||||||
|
isIndexed: Bool = false,
|
||||||
|
isTransient: Bool = false,
|
||||||
|
versionHashModifier: String? = nil,
|
||||||
|
renamingIdentifier: String? = nil,
|
||||||
|
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
|
||||||
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
|
||||||
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
self.isIndexed = isIndexed
|
self.isIndexed = isIndexed
|
||||||
self.isTransient = isTransient
|
self.isTransient = isTransient
|
||||||
self.defaultValue = `default`.cs_toImportableNativeType()
|
self.defaultValue = { `default`().cs_toImportableNativeType() }
|
||||||
self.versionHashModifier = versionHashModifier
|
self.versionHashModifier = versionHashModifier
|
||||||
self.renamingIdentifier = renamingIdentifier
|
self.renamingIdentifier = renamingIdentifier
|
||||||
self.customGetter = customGetter
|
self.customGetter = customGetter
|
||||||
self.customSetter = customSetter
|
self.customSetter = customSetter
|
||||||
|
self.affectedByKeyPaths = affectedByKeyPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,45 +145,103 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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 object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { V.cs_fromImportableNativeType($0 as! V.ImportableNativeType)! }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
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."
|
||||||
|
)
|
||||||
|
let customGetter = (self.customGetter ?? { $1() })
|
||||||
|
return customGetter(
|
||||||
|
object,
|
||||||
|
{ () -> V in
|
||||||
|
|
||||||
|
return V.cs_fromImportableNativeType(
|
||||||
|
object.rawObject!.value(forKey: self.keyPath)! as! V.ImportableNativeType
|
||||||
|
)!
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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."
|
||||||
|
)
|
||||||
|
let customSetter = (self.customSetter ?? { $1($2) })
|
||||||
|
customSetter(
|
||||||
|
object,
|
||||||
|
{ (newValue: V) -> Void in
|
||||||
|
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue.cs_toImportableNativeType(),
|
||||||
|
forKey: self.keyPath
|
||||||
|
)
|
||||||
|
},
|
||||||
|
newValue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The primitive value. Compared to `value`, `primitiveValue` bypasses all notification mechanisms. This is typically only used for setting values for transient properties.
|
||||||
|
*/
|
||||||
|
public var primitiveValue: V {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isEditableInContext() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
self.customSetter(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object,
|
|
||||||
{ (newValue: V) -> Void in
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
object.rawObject!.setValue(
|
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
|
||||||
newValue,
|
)
|
||||||
forKvcKey: self.keyPath,
|
return V.cs_fromImportableNativeType(
|
||||||
willSetValue: { $0.cs_toImportableNativeType() }
|
object.rawObject!.primitiveValue(forKey: self.keyPath)! as! V.ImportableNativeType
|
||||||
)
|
)!
|
||||||
},
|
}
|
||||||
newValue
|
}
|
||||||
|
set {
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
self.parentObject != nil,
|
||||||
|
"Attempted to access primitive 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 primitive value outside it's designated queue."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
self.isTransient || object.rawObject!.isEditableInContext() == true,
|
||||||
|
"Attempted to update a \(cs_typeName(O.self))'s primitive value from outside a transaction."
|
||||||
|
)
|
||||||
|
object.rawObject!.setPrimitiveValue(
|
||||||
|
newValue.cs_toImportableNativeType(),
|
||||||
|
forKey: self.keyPath
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,20 +258,65 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
internal let isOptional = false
|
internal let isOptional = false
|
||||||
internal let isIndexed: Bool
|
internal let isIndexed: Bool
|
||||||
internal let isTransient: Bool
|
internal let isTransient: Bool
|
||||||
internal let defaultValue: Any?
|
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: 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
|
||||||
|
let value = customGetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{
|
||||||
|
rawObject.getValue(
|
||||||
|
forKvcKey: keyPath,
|
||||||
|
didGetValue: { V.cs_fromImportableNativeType($0 as! V.ImportableNativeType!)! }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
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
|
||||||
|
customSetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ (userValue: V) -> Void in
|
||||||
|
|
||||||
|
rawObject.setValue(
|
||||||
|
userValue,
|
||||||
|
forKvcKey: keyPath,
|
||||||
|
willSetValue: { $0.cs_toImportableNativeType() }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
V.cs_fromImportableNativeType(newValue as! V.ImportableNativeType)!
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
|
private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)?
|
||||||
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
|
private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -247,17 +361,28 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
- parameter setValue: the original setter for the property
|
- parameter setValue: the original setter for the property
|
||||||
- parameter finalNewValue: the transformed new value
|
- parameter finalNewValue: the transformed new value
|
||||||
- parameter originalNewValue: the original 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,
|
||||||
|
`default`: @autoclosure @escaping () -> V? = nil,
|
||||||
|
isIndexed: Bool = false,
|
||||||
|
isTransient: Bool = false,
|
||||||
|
versionHashModifier: String? = nil,
|
||||||
|
renamingIdentifier: String? = nil,
|
||||||
|
customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil,
|
||||||
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil,
|
||||||
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
self.isIndexed = isIndexed
|
self.isIndexed = isIndexed
|
||||||
self.isTransient = isTransient
|
self.isTransient = isTransient
|
||||||
self.defaultValue = `default`?.cs_toImportableNativeType()
|
self.defaultValue = { `default`()?.cs_toImportableNativeType() }
|
||||||
self.versionHashModifier = versionHashModifier
|
self.versionHashModifier = versionHashModifier
|
||||||
self.renamingIdentifier = renamingIdentifier
|
self.renamingIdentifier = renamingIdentifier
|
||||||
self.customGetter = customGetter
|
self.customGetter = customGetter
|
||||||
self.customSetter = customSetter
|
self.customSetter = customSetter
|
||||||
|
self.affectedByKeyPaths = affectedByKeyPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -267,45 +392,101 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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 object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { ($0 as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
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."
|
||||||
|
)
|
||||||
|
let customGetter = (self.customGetter ?? { $1() })
|
||||||
|
return customGetter(
|
||||||
|
object,
|
||||||
|
{ () -> V? in
|
||||||
|
|
||||||
|
return (object.rawObject!.value(forKey: self.keyPath) as! V.ImportableNativeType?)
|
||||||
|
.flatMap(V.cs_fromImportableNativeType)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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."
|
||||||
|
)
|
||||||
|
let customSetter = (self.customSetter ?? { $1($2) })
|
||||||
|
customSetter(
|
||||||
|
object,
|
||||||
|
{ (newValue: V?) -> Void in
|
||||||
|
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue?.cs_toImportableNativeType(),
|
||||||
|
forKey: self.keyPath
|
||||||
|
)
|
||||||
|
},
|
||||||
|
newValue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The primitive value. Compared to `value`, `primitiveValue` bypasses all notification mechanisms. This is typically only used for setting values for transient properties.
|
||||||
|
*/
|
||||||
|
public var primitiveValue: V? {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isEditableInContext() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
self.customSetter(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object,
|
|
||||||
{ (newValue: V?) -> Void in
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
object.rawObject!.setValue(
|
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
|
||||||
newValue,
|
)
|
||||||
forKvcKey: self.keyPath,
|
return (object.rawObject!.primitiveValue(forKey: self.keyPath) as! V.ImportableNativeType?)
|
||||||
willSetValue: { $0?.cs_toImportableNativeType() }
|
.flatMap(V.cs_fromImportableNativeType)
|
||||||
)
|
}
|
||||||
},
|
}
|
||||||
newValue
|
set {
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
self.parentObject != nil,
|
||||||
|
"Attempted to access primitive 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 primitive value outside it's designated queue."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
self.isTransient || object.rawObject!.isEditableInContext() == true,
|
||||||
|
"Attempted to update a \(cs_typeName(O.self))'s primitive value from outside a transaction."
|
||||||
|
)
|
||||||
|
object.rawObject!.setPrimitiveValue(
|
||||||
|
newValue?.cs_toImportableNativeType(),
|
||||||
|
forKey: self.keyPath
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,20 +502,65 @@ public enum ValueContainer<O: CoreStoreObject> {
|
|||||||
internal let isOptional = true
|
internal let isOptional = true
|
||||||
internal let isIndexed: Bool
|
internal let isIndexed: Bool
|
||||||
internal let isTransient: Bool
|
internal let isTransient: Bool
|
||||||
internal let defaultValue: Any?
|
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: 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
|
||||||
|
let value = customGetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{
|
||||||
|
rawObject.getValue(
|
||||||
|
forKvcKey: keyPath,
|
||||||
|
didGetValue: { ($0 as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
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
|
||||||
|
customSetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ (userValue: V?) -> Void in
|
||||||
|
|
||||||
|
rawObject.setValue(
|
||||||
|
userValue,
|
||||||
|
forKvcKey: keyPath,
|
||||||
|
willSetValue: { $0?.cs_toImportableNativeType() }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
(newValue as! V.ImportableNativeType?).flatMap(V.cs_fromImportableNativeType)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
|
private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)?
|
||||||
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
|
private let customSetter: ((_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,8 +585,17 @@ public extension ValueContainer.Required where V: EmptyableAttributeType {
|
|||||||
- parameter setValue: the original setter for the property
|
- parameter setValue: the original setter for the property
|
||||||
- parameter finalNewValue: the transformed new value
|
- parameter finalNewValue: the transformed new value
|
||||||
- parameter originalNewValue: the original 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 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) }) {
|
public convenience init(
|
||||||
|
_ keyPath: KeyPath,
|
||||||
|
isIndexed: Bool = false,
|
||||||
|
isTransient: Bool = false,
|
||||||
|
versionHashModifier: String? = nil,
|
||||||
|
renamingIdentifier: String? = nil,
|
||||||
|
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
|
||||||
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
|
||||||
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.init(
|
self.init(
|
||||||
keyPath,
|
keyPath,
|
||||||
@@ -370,7 +605,8 @@ public extension ValueContainer.Required where V: EmptyableAttributeType {
|
|||||||
versionHashModifier: versionHashModifier,
|
versionHashModifier: versionHashModifier,
|
||||||
renamingIdentifier: renamingIdentifier,
|
renamingIdentifier: renamingIdentifier,
|
||||||
customGetter: customGetter,
|
customGetter: customGetter,
|
||||||
customSetter: customSetter
|
customSetter: customSetter,
|
||||||
|
affectedByKeyPaths: affectedByKeyPaths
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -425,8 +661,18 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
- parameter setValue: the original setter for the property
|
- parameter setValue: the original setter for the property
|
||||||
- parameter finalNewValue: the transformed new value
|
- parameter finalNewValue: the transformed new value
|
||||||
- parameter originalNewValue: the original 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, 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,
|
||||||
|
`default`: @autoclosure @escaping () -> V,
|
||||||
|
isIndexed: Bool = false,
|
||||||
|
isTransient: Bool = false,
|
||||||
|
versionHashModifier: String? = nil,
|
||||||
|
renamingIdentifier: String? = nil,
|
||||||
|
customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)? = nil,
|
||||||
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void)? = nil,
|
||||||
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
self.defaultValue = `default`
|
self.defaultValue = `default`
|
||||||
@@ -436,6 +682,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
self.renamingIdentifier = renamingIdentifier
|
self.renamingIdentifier = renamingIdentifier
|
||||||
self.customGetter = customGetter
|
self.customGetter = customGetter
|
||||||
self.customSetter = customSetter
|
self.customSetter = customSetter
|
||||||
|
self.affectedByKeyPaths = affectedByKeyPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -445,44 +692,99 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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 object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { $0 as! V }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
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."
|
||||||
|
)
|
||||||
|
let customGetter = (self.customGetter ?? { $1() })
|
||||||
|
return customGetter(
|
||||||
|
object,
|
||||||
|
{ () -> V in
|
||||||
|
|
||||||
|
return object.rawObject!.value(forKey: self.keyPath)! as! V
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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."
|
||||||
|
)
|
||||||
|
let customSetter = (self.customSetter ?? { $1($2) })
|
||||||
|
customSetter(
|
||||||
|
object,
|
||||||
|
{ (newValue: V) -> Void in
|
||||||
|
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue,
|
||||||
|
forKey: self.keyPath
|
||||||
|
)
|
||||||
|
},
|
||||||
|
newValue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The primitive value. Compared to `value`, `primitiveValue` bypasses all notification mechanisms. This is typically only used for setting values for transient properties.
|
||||||
|
*/
|
||||||
|
public var primitiveValue: V {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isEditableInContext() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
self.customSetter(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object,
|
|
||||||
{ (newValue: V) -> Void in
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
object.rawObject!.setValue(
|
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
|
||||||
newValue,
|
)
|
||||||
forKvcKey: self.keyPath
|
return object.rawObject!.primitiveValue(forKey: self.keyPath)! as! V
|
||||||
)
|
}
|
||||||
},
|
}
|
||||||
newValue
|
set {
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
self.parentObject != nil,
|
||||||
|
"Attempted to access primitive 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 primitive value outside it's designated queue."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
self.isTransient || object.rawObject!.isEditableInContext() == true,
|
||||||
|
"Attempted to update a \(cs_typeName(O.self))'s primitive value from outside a transaction."
|
||||||
|
)
|
||||||
|
object.rawObject!.setPrimitiveValue(
|
||||||
|
newValue,
|
||||||
|
forKey: self.keyPath
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,20 +801,55 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
internal let isOptional = false
|
internal let isOptional = false
|
||||||
internal let isIndexed: Bool
|
internal let isIndexed: Bool
|
||||||
internal let isTransient: Bool
|
internal let isTransient: Bool
|
||||||
internal let defaultValue: Any?
|
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: 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
|
||||||
|
return customGetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ rawObject.getValue(forKvcKey: keyPath) as! V }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
customSetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ (userValue: V) -> Void in
|
||||||
|
|
||||||
|
rawObject.setValue(userValue, forKvcKey: keyPath)
|
||||||
|
},
|
||||||
|
newValue as! V
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: (_ `self`: O, _ getValue: () -> V) -> V
|
private let customGetter: ((_ `self`: O, _ getValue: () -> V) -> V)?
|
||||||
private let customSetter: (_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void
|
private let customSetter: ((_ `self`: O, _ setValue: (V) -> Void, _ newValue: V) -> Void)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -551,8 +888,18 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
- parameter setValue: the original setter for the property
|
- parameter setValue: the original setter for the property
|
||||||
- parameter finalNewValue: the transformed new value
|
- parameter finalNewValue: the transformed new value
|
||||||
- parameter originalNewValue: the original 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,
|
||||||
|
`default`: @autoclosure @escaping () -> V? = nil,
|
||||||
|
isIndexed: Bool = false,
|
||||||
|
isTransient: Bool = false,
|
||||||
|
versionHashModifier: String? = nil,
|
||||||
|
renamingIdentifier: String? = nil,
|
||||||
|
customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)? = nil,
|
||||||
|
customSetter: ((_ `self`: O, _ setValue: (_ finalNewValue: V?) -> Void, _ originalNewValue: V?) -> Void)? = nil,
|
||||||
|
affectedByKeyPaths: @autoclosure @escaping () -> Set<String> = []) {
|
||||||
|
|
||||||
self.keyPath = keyPath
|
self.keyPath = keyPath
|
||||||
self.defaultValue = `default`
|
self.defaultValue = `default`
|
||||||
@@ -562,6 +909,7 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
self.renamingIdentifier = renamingIdentifier
|
self.renamingIdentifier = renamingIdentifier
|
||||||
self.customGetter = customGetter
|
self.customGetter = customGetter
|
||||||
self.customSetter = customSetter
|
self.customSetter = customSetter
|
||||||
|
self.affectedByKeyPaths = affectedByKeyPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -571,44 +919,99 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
|
|
||||||
get {
|
get {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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 object.rawObject!.getValue(
|
|
||||||
forKvcKey: self.keyPath,
|
|
||||||
didGetValue: { $0 as! V? }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
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."
|
||||||
|
)
|
||||||
|
let customGetter = (self.customGetter ?? { $1() })
|
||||||
|
return customGetter(
|
||||||
|
object,
|
||||||
|
{ () -> V? in
|
||||||
|
|
||||||
|
object.rawObject!.value(forKey: self.keyPath) as! V?
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
|
|
||||||
let object = self.parentObject() as! O
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isRunningInAllowedQueue() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
"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."
|
||||||
|
)
|
||||||
|
let customSetter = (self.customSetter ?? { $1($2) })
|
||||||
|
customSetter(
|
||||||
|
object,
|
||||||
|
{ (newValue: V?) -> Void in
|
||||||
|
|
||||||
|
object.rawObject!.setValue(
|
||||||
|
newValue,
|
||||||
|
forKey: self.keyPath
|
||||||
|
)
|
||||||
|
},
|
||||||
|
newValue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The primitive value. Compared to `value`, `primitiveValue` bypasses all notification mechanisms. This is typically only used for setting values for transient properties.
|
||||||
|
*/
|
||||||
|
public var primitiveValue: V? {
|
||||||
|
|
||||||
|
get {
|
||||||
|
|
||||||
CoreStore.assert(
|
CoreStore.assert(
|
||||||
object.rawObject!.isEditableInContext() == true,
|
self.parentObject != nil,
|
||||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
"Attempted to access primitive values from a \(cs_typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||||
)
|
)
|
||||||
self.customSetter(
|
return withExtendedLifetime(self.parentObject! as! O) { (object: O) in
|
||||||
object,
|
|
||||||
{ (newValue: V?) -> Void in
|
CoreStore.assert(
|
||||||
|
object.rawObject!.isRunningInAllowedQueue() == true,
|
||||||
object.rawObject!.setValue(
|
"Attempted to access \(cs_typeName(O.self))'s primitive value outside it's designated queue."
|
||||||
newValue,
|
)
|
||||||
forKvcKey: self.keyPath
|
return object.rawObject!.primitiveValue(forKey: self.keyPath) as! V?
|
||||||
)
|
}
|
||||||
},
|
}
|
||||||
newValue
|
set {
|
||||||
|
|
||||||
|
CoreStore.assert(
|
||||||
|
self.parentObject != nil,
|
||||||
|
"Attempted to access primitive 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 primitive value outside it's designated queue."
|
||||||
|
)
|
||||||
|
CoreStore.assert(
|
||||||
|
self.isTransient || object.rawObject!.isEditableInContext() == true,
|
||||||
|
"Attempted to update a \(cs_typeName(O.self))'s primitive value from outside a transaction."
|
||||||
|
)
|
||||||
|
object.rawObject!.setPrimitiveValue(
|
||||||
|
newValue,
|
||||||
|
forKey: self.keyPath
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,20 +1028,63 @@ public enum TransformableContainer<O: CoreStoreObject> {
|
|||||||
internal let isOptional = true
|
internal let isOptional = true
|
||||||
internal let isIndexed: Bool
|
internal let isIndexed: Bool
|
||||||
internal let isTransient: Bool
|
internal let isTransient: Bool
|
||||||
internal let defaultValue: Any?
|
|
||||||
internal let versionHashModifier: String?
|
internal let versionHashModifier: String?
|
||||||
internal let renamingIdentifier: 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
|
||||||
|
return customGetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ rawObject.getValue(forKvcKey: keyPath) as! V? }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal private(set) lazy var setter: CoreStoreManagedObject.CustomSetter? = cs_lazy { [unowned self] in
|
||||||
|
|
||||||
|
let keyPath = self.keyPath
|
||||||
|
guard let customSetter = self.customSetter else {
|
||||||
|
|
||||||
|
guard let _ = self.customGetter else {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
rawObject.setValue(newValue, forKvcKey: keyPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { (_ id: Any, _ newValue: Any?) -> Void in
|
||||||
|
|
||||||
|
let rawObject = id as! CoreStoreManagedObject
|
||||||
|
customSetter(
|
||||||
|
O.cs_fromRaw(object: rawObject),
|
||||||
|
{ (userValue: V?) -> Void in
|
||||||
|
|
||||||
|
rawObject.setValue(userValue, forKvcKey: keyPath)
|
||||||
|
},
|
||||||
|
newValue as! V?
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
private let customGetter: (_ `self`: O, _ getValue: () -> V?) -> V?
|
private let customGetter: ((_ `self`: O, _ getValue: () -> V?) -> V?)?
|
||||||
private let customSetter: (_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void
|
private let customSetter: ((_ `self`: O, _ setValue: (V?) -> Void, _ newValue: V?) -> Void)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -941,8 +1387,11 @@ internal protocol AttributeProtocol: class {
|
|||||||
var isOptional: Bool { get }
|
var isOptional: Bool { get }
|
||||||
var isIndexed: Bool { get }
|
var isIndexed: Bool { get }
|
||||||
var isTransient: Bool { get }
|
var isTransient: Bool { get }
|
||||||
var defaultValue: Any? { get }
|
|
||||||
var versionHashModifier: String? { get }
|
var versionHashModifier: String? { get }
|
||||||
var renamingIdentifier: String? { get }
|
var renamingIdentifier: String? { get }
|
||||||
var parentObject: () -> CoreStoreObject { get set }
|
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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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`.
|
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.
|
The source model version for the mapping.
|
||||||
|
|||||||
Reference in New Issue
Block a user