mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-15 05:33:31 +01:00
Field.Relationship propertyWrapper
This commit is contained in:
@@ -613,6 +613,18 @@
|
||||
B57D27C21D0BC20100539C58 /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57D27C11D0BC20100539C58 /* QueryTests.swift */; };
|
||||
B57D27C31D0BC20100539C58 /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57D27C11D0BC20100539C58 /* QueryTests.swift */; };
|
||||
B57D27C41D0BC20100539C58 /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57D27C11D0BC20100539C58 /* QueryTests.swift */; };
|
||||
B57E6FA223D302FA000FD031 /* Field.Relationship.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FA123D302FA000FD031 /* Field.Relationship.swift */; };
|
||||
B57E6FA323D302FA000FD031 /* Field.Relationship.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FA123D302FA000FD031 /* Field.Relationship.swift */; };
|
||||
B57E6FA423D302FA000FD031 /* Field.Relationship.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FA123D302FA000FD031 /* Field.Relationship.swift */; };
|
||||
B57E6FA523D302FA000FD031 /* Field.Relationship.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FA123D302FA000FD031 /* Field.Relationship.swift */; };
|
||||
B57E6FA723D305D6000FD031 /* FIeldRelationshipType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FA623D305D6000FD031 /* FIeldRelationshipType.swift */; };
|
||||
B57E6FA823D305D6000FD031 /* FIeldRelationshipType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FA623D305D6000FD031 /* FIeldRelationshipType.swift */; };
|
||||
B57E6FA923D305D6000FD031 /* FIeldRelationshipType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FA623D305D6000FD031 /* FIeldRelationshipType.swift */; };
|
||||
B57E6FAA23D305D6000FD031 /* FIeldRelationshipType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FA623D305D6000FD031 /* FIeldRelationshipType.swift */; };
|
||||
B57E6FAC23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FAB23D30A5B000FD031 /* FieldRelationshipProtocol.swift */; };
|
||||
B57E6FAD23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FAB23D30A5B000FD031 /* FieldRelationshipProtocol.swift */; };
|
||||
B57E6FAE23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FAB23D30A5B000FD031 /* FieldRelationshipProtocol.swift */; };
|
||||
B57E6FAF23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57E6FAB23D30A5B000FD031 /* FieldRelationshipProtocol.swift */; };
|
||||
B580857A1CDF808C004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; };
|
||||
B580857B1CDF808D004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; };
|
||||
B580857C1CDF808F004C2EEB /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; };
|
||||
@@ -1113,6 +1125,9 @@
|
||||
B56E4EE323CEDF0900E1708C /* Field.Computed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Field.Computed.swift; sourceTree = "<group>"; };
|
||||
B57D27BD1D0BBE8200539C58 /* BaseTestDataTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTestDataTestCase.swift; sourceTree = "<group>"; };
|
||||
B57D27C11D0BC20100539C58 /* QueryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryTests.swift; sourceTree = "<group>"; };
|
||||
B57E6FA123D302FA000FD031 /* Field.Relationship.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Field.Relationship.swift; sourceTree = "<group>"; };
|
||||
B57E6FA623D305D6000FD031 /* FIeldRelationshipType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FIeldRelationshipType.swift; sourceTree = "<group>"; };
|
||||
B57E6FAB23D30A5B000FD031 /* FieldRelationshipProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FieldRelationshipProtocol.swift; sourceTree = "<group>"; };
|
||||
B58085741CDF7F00004C2EEB /* SetupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetupTests.swift; sourceTree = "<group>"; };
|
||||
B581B9312362BB8C002BDB2B /* ObjectPublisherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectPublisherTests.swift; sourceTree = "<group>"; };
|
||||
B5831B6F1F34AC3400A9F647 /* AttributeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributeProtocol.swift; sourceTree = "<group>"; };
|
||||
@@ -1461,6 +1476,7 @@
|
||||
B5831B741F34AC7A00A9F647 /* RelationshipProtocol.swift */,
|
||||
B56E4ED323CDB54A00E1708C /* FieldProtocol.swift */,
|
||||
B50C3ED923D0545700B29880 /* FieldAttributeProtocol.swift */,
|
||||
B57E6FAB23D30A5B000FD031 /* FieldRelationshipProtocol.swift */,
|
||||
B50564D22350CC3100482308 /* PropertyProtocol.swift */,
|
||||
B53D9E5823513712000F48FB /* DiffableDataSourceSnapshotProtocol.swift */,
|
||||
B50E175623517DE4004F033C /* Differentiable.swift */,
|
||||
@@ -1608,6 +1624,7 @@
|
||||
B56E4ECE23CD9E4200E1708C /* Field.Stored.swift */,
|
||||
B56E4EE323CEDF0900E1708C /* Field.Computed.swift */,
|
||||
B50C3EE423D153EA00B29880 /* Field.Coded.swift */,
|
||||
B57E6FA123D302FA000FD031 /* Field.Relationship.swift */,
|
||||
B50C3EDE23D05BB200B29880 /* Coders */,
|
||||
B56E4EDD23CEBB0400E1708C /* Supported Values */,
|
||||
);
|
||||
@@ -1619,6 +1636,7 @@
|
||||
children = (
|
||||
B56E4EDE23CEBCF000E1708C /* FieldOptionalType.swift */,
|
||||
B56E4ED823CEB8E700E1708C /* FieldStorableType.swift */,
|
||||
B57E6FA623D305D6000FD031 /* FIeldRelationshipType.swift */,
|
||||
);
|
||||
name = "Supported Values";
|
||||
sourceTree = "<group>";
|
||||
@@ -2301,6 +2319,7 @@
|
||||
B5C976E31C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift in Sources */,
|
||||
B53FBA121CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
||||
B5831B751F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B57E6FA723D305D6000FD031 /* FIeldRelationshipType.swift in Sources */,
|
||||
B51B5C2D22D43E38009FA3BA /* KeyPath+KeyPaths.swift in Sources */,
|
||||
B50564D32350CC3100482308 /* PropertyProtocol.swift in Sources */,
|
||||
B5D8CA762346E7590055D7D1 /* DataStack+DataSources.swift in Sources */,
|
||||
@@ -2435,6 +2454,7 @@
|
||||
B5A991EC1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */,
|
||||
B5FE4DA71C84FB4400FA6A91 /* InMemoryStore.swift in Sources */,
|
||||
B52F743D1E9B8724005F3DAC /* DynamicSchema.swift in Sources */,
|
||||
B57E6FA223D302FA000FD031 /* Field.Relationship.swift in Sources */,
|
||||
B5E8A72021C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
||||
B53D9E5923513712000F48FB /* DiffableDataSourceSnapshotProtocol.swift in Sources */,
|
||||
B56E4ED923CEB8E700E1708C /* FieldStorableType.swift in Sources */,
|
||||
@@ -2446,6 +2466,7 @@
|
||||
B5E834B91B76311F001D3D50 /* BaseDataTransaction+Importing.swift in Sources */,
|
||||
B5E84EE61AFF84610064E85B /* DefaultLogger.swift in Sources */,
|
||||
B53FBA041CAB300C00F0D40A /* CSMigrationType.swift in Sources */,
|
||||
B57E6FAC23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */,
|
||||
B509D7C923C8491C00F42824 /* Relationship.ToManyOrdered.swift in Sources */,
|
||||
B5E84EF41AFF846E0064E85B /* AsynchronousDataTransaction.swift in Sources */,
|
||||
B50C3EEF23D1605C00B29880 /* FieldCoders.DefaultNSSecureCoding.swift in Sources */,
|
||||
@@ -2549,6 +2570,7 @@
|
||||
B53FBA141CAB63CB00F0D40A /* Progress+ObjectiveC.swift in Sources */,
|
||||
B5831B761F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B5BF7FB3234C97910070E741 /* DiffableDataSource.swift in Sources */,
|
||||
B57E6FA823D305D6000FD031 /* FIeldRelationshipType.swift in Sources */,
|
||||
B5E1B5AA1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
||||
B5D339F21E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
B5E1B59F1CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */,
|
||||
@@ -2683,6 +2705,7 @@
|
||||
B5AA37F2235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */,
|
||||
B50E175D2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||
B5474D162227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
||||
B57E6FA323D302FA000FD031 /* Field.Relationship.swift in Sources */,
|
||||
B501322B2346A9AE00FC238B /* ListPublisher.swift in Sources */,
|
||||
B56924001EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */,
|
||||
B56E4EDA23CEB8E700E1708C /* FieldStorableType.swift in Sources */,
|
||||
@@ -2694,6 +2717,7 @@
|
||||
82BA18BE1C4BBD4A00A0916E /* Tweak.swift in Sources */,
|
||||
B509D7C523C848DA00F42824 /* Relationship.ToOne.swift in Sources */,
|
||||
B5DBE2CE1C9914A900B5CEFA /* CSCoreStore.swift in Sources */,
|
||||
B57E6FAD23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */,
|
||||
B5831B711F34AC3400A9F647 /* AttributeProtocol.swift in Sources */,
|
||||
B546F95E1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */,
|
||||
B50C3EF023D1605C00B29880 /* FieldCoders.DefaultNSSecureCoding.swift in Sources */,
|
||||
@@ -2797,6 +2821,7 @@
|
||||
B5BF7FCE234D80910070E741 /* Internals.LazyNonmutating.swift in Sources */,
|
||||
B52DD19E1BE1F92C00949AFE /* AsynchronousDataTransaction.swift in Sources */,
|
||||
B50564D62350CC3100482308 /* PropertyProtocol.swift in Sources */,
|
||||
B57E6FAA23D305D6000FD031 /* FIeldRelationshipType.swift in Sources */,
|
||||
B5831B781F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B52DD1981BE1F92500949AFE /* CoreStore+Setup.swift in Sources */,
|
||||
B5D339F41E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
@@ -2931,6 +2956,7 @@
|
||||
18166886232B9ED20097C275 /* KeyPath+KeyPaths.swift in Sources */,
|
||||
B5E8A72321C1015300EF006A /* CoreStoreObject+Observing.swift in Sources */,
|
||||
B50E175F2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||
B57E6FA523D302FA000FD031 /* Field.Relationship.swift in Sources */,
|
||||
B5474D182227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
||||
B501322E2346A9B100FC238B /* ListPublisher.swift in Sources */,
|
||||
B56E4EDC23CEB8E700E1708C /* FieldStorableType.swift in Sources */,
|
||||
@@ -2942,6 +2968,7 @@
|
||||
B52DD1B91BE1F94000949AFE /* CoreStore+Migration.swift in Sources */,
|
||||
B509D7C723C848DA00F42824 /* Relationship.ToOne.swift in Sources */,
|
||||
B5519A5C1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */,
|
||||
B57E6FAF23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */,
|
||||
B5DBE2D51C991B3E00B5CEFA /* CSDataStack.swift in Sources */,
|
||||
B5831B731F34AC3400A9F647 /* AttributeProtocol.swift in Sources */,
|
||||
B50C3EF223D1605C00B29880 /* FieldCoders.DefaultNSSecureCoding.swift in Sources */,
|
||||
@@ -3045,6 +3072,7 @@
|
||||
B5E1B5AB1CAA49E2007FD580 /* CSDataStack+Migrating.swift in Sources */,
|
||||
B5831B771F34AC7A00A9F647 /* RelationshipProtocol.swift in Sources */,
|
||||
B5BF7FB4234C97910070E741 /* DiffableDataSource.swift in Sources */,
|
||||
B57E6FA923D305D6000FD031 /* FIeldRelationshipType.swift in Sources */,
|
||||
B5D339F31E94AF5800C880DE /* CoreStoreStrings.swift in Sources */,
|
||||
B5E1B5A01CAA2568007FD580 /* CSDataStack+Observing.swift in Sources */,
|
||||
B5ECDC261CA81A3900C7F112 /* CSCoreStore+Querying.swift in Sources */,
|
||||
@@ -3179,6 +3207,7 @@
|
||||
B5AA37F3235C28EE00FFD4B9 /* DiffableDataSource.CollectionViewAdapter-AppKit.swift in Sources */,
|
||||
B50E175E2351848E004F033C /* Internals.DiffableDataUIDispatcher.DiffResult.swift in Sources */,
|
||||
B5474D172227C08700B21FEC /* Internals.CoreStoreFetchRequest.swift in Sources */,
|
||||
B57E6FA423D302FA000FD031 /* Field.Relationship.swift in Sources */,
|
||||
B501322D2346A9B000FC238B /* ListPublisher.swift in Sources */,
|
||||
B56924011EB82976007C4DC9 /* CSUnsafeDataModelSchema.swift in Sources */,
|
||||
B56E4EDB23CEB8E700E1708C /* FieldStorableType.swift in Sources */,
|
||||
@@ -3190,6 +3219,7 @@
|
||||
B56321841BD65216006C9394 /* DefaultLogger.swift in Sources */,
|
||||
B509D7C623C848DA00F42824 /* Relationship.ToOne.swift in Sources */,
|
||||
B5DBE2CF1C9914A900B5CEFA /* CSCoreStore.swift in Sources */,
|
||||
B57E6FAE23D30A5B000FD031 /* FieldRelationshipProtocol.swift in Sources */,
|
||||
B5831B721F34AC3400A9F647 /* AttributeProtocol.swift in Sources */,
|
||||
B546F95F1C9A12B800D5AC55 /* CSSQliteStore.swift in Sources */,
|
||||
B50C3EF123D1605C00B29880 /* FieldCoders.DefaultNSSecureCoding.swift in Sources */,
|
||||
|
||||
@@ -44,7 +44,8 @@ class Animal: CoreStoreObject {
|
||||
@Field.Coded("color", coder: FieldCoders.NSCoding.self)
|
||||
var color: Color? = .blue
|
||||
|
||||
let master = Relationship.ToOne<Person>("master")
|
||||
@Field.Relationship("master")
|
||||
var master: Person?
|
||||
}
|
||||
|
||||
class Dog: Animal {
|
||||
@@ -52,15 +53,45 @@ class Dog: Animal {
|
||||
@Field.Stored("nickname")
|
||||
var nickname: String?
|
||||
|
||||
let age = Value.Required<Int>("age", initial: 1)
|
||||
let friends = Relationship.ToManyOrdered<Dog>("friends")
|
||||
let friendedBy = Relationship.ToManyUnordered<Dog>("friendedBy", inverse: { $0.friends })
|
||||
@Field.Stored("age")
|
||||
var age: Int = 1
|
||||
|
||||
@Field.Relationship("friends")
|
||||
var friends: [Dog]
|
||||
|
||||
@Field.Relationship("friendedBy", inverse: \.$friends)
|
||||
var friendedBy: Set<Dog>
|
||||
}
|
||||
|
||||
struct CustomType {
|
||||
var string = "customString"
|
||||
}
|
||||
|
||||
enum Job: String {
|
||||
|
||||
case unemployed
|
||||
case engineer
|
||||
case doctor
|
||||
case lawyer
|
||||
|
||||
init?(data: Data) {
|
||||
|
||||
guard
|
||||
let rawValue = String(data: data, encoding: .utf8),
|
||||
let value = Self.init(rawValue: rawValue)
|
||||
else {
|
||||
|
||||
return nil
|
||||
}
|
||||
self = value
|
||||
}
|
||||
|
||||
func toData() -> Data {
|
||||
|
||||
return Data(self.rawValue.utf8)
|
||||
}
|
||||
}
|
||||
|
||||
class Person: CoreStoreObject {
|
||||
|
||||
@Field.Stored("title", customSetter: Person.setTitle(_:_:))
|
||||
@@ -84,47 +115,25 @@ class Person: CoreStoreObject {
|
||||
|
||||
@Field.Coded(
|
||||
"job", coder: (
|
||||
encode: { $0.data },
|
||||
encode: { $0.toData() },
|
||||
decode: { $0.flatMap(Job.init(data:)) ?? .unemployed }
|
||||
)
|
||||
)
|
||||
var job: Job = .unemployed
|
||||
|
||||
let spouse = Relationship.ToOne<Person>("spouse")
|
||||
|
||||
let pets = Relationship.ToManyUnordered<Animal>("pets", inverse: { $0.master })
|
||||
@Field.Relationship("spouse")
|
||||
var spouse: Person?
|
||||
|
||||
private let _spouse = Relationship.ToOne<Person>("_spouseInverse", inverse: { $0.spouse })
|
||||
@Field.Relationship("pets", inverse: \.$master)
|
||||
var pets: Set<Animal>
|
||||
|
||||
enum Job: String {
|
||||
|
||||
case unemployed
|
||||
case engineer
|
||||
case doctor
|
||||
case lawyer
|
||||
|
||||
init?(data: Data) {
|
||||
|
||||
guard
|
||||
let rawValue = String(data: data, encoding: .utf8),
|
||||
let value = Self.init(rawValue: rawValue)
|
||||
else {
|
||||
|
||||
return nil
|
||||
}
|
||||
self = value
|
||||
}
|
||||
|
||||
var data: Data {
|
||||
|
||||
return Data(self.rawValue.utf8)
|
||||
}
|
||||
}
|
||||
@Field.Relationship("_spouseInverse", inverse: \.$spouse)
|
||||
private var spouseInverse: Person?
|
||||
|
||||
private static func setTitle(_ partialObject: PartialObject<Person>, _ newValue: String) {
|
||||
|
||||
partialObject.setPrimitiveValue(newValue, for: \.$title)
|
||||
partialObject.setPrimitiveValue(nil, for: { $0.$displayName })
|
||||
partialObject.setPrimitiveValue(nil, for: \.$displayName)
|
||||
}
|
||||
|
||||
private static func setName(_ partialObject: PartialObject<Person>, _ newValue: String) {
|
||||
@@ -226,8 +235,8 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
case String(keyPath: \Animal.$species):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Stored<String>)
|
||||
|
||||
case String(keyPath: \Animal.master):
|
||||
XCTAssertTrue(property is RelationshipContainer<Animal>.ToOne<Person>)
|
||||
case String(keyPath: \Animal.$master):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Relationship<Person?>)
|
||||
|
||||
case String(keyPath: \Animal.$color):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Coded<Color?>)
|
||||
@@ -240,7 +249,7 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
let dog = transaction.create(Into<Dog>())
|
||||
XCTAssertEqual(dog.species, "Swift")
|
||||
XCTAssertEqual(dog.nickname, nil)
|
||||
XCTAssertEqual(dog.age.value, 1)
|
||||
XCTAssertEqual(dog.age, 1)
|
||||
|
||||
for property in Dog.metaProperties(includeSuperclasses: true) {
|
||||
|
||||
@@ -249,8 +258,8 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
case String(keyPath: \Dog.$species):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Stored<String>)
|
||||
|
||||
case String(keyPath: \Dog.master):
|
||||
XCTAssertTrue(property is RelationshipContainer<Animal>.ToOne<Person>)
|
||||
case String(keyPath: \Dog.$master):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Relationship<Person?>)
|
||||
|
||||
case String(keyPath: \Dog.$color):
|
||||
XCTAssertTrue(property is FieldContainer<Animal>.Coded<Color?>)
|
||||
@@ -258,14 +267,14 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
case String(keyPath: \Dog.$nickname):
|
||||
XCTAssertTrue(property is FieldContainer<Dog>.Stored<String?>)
|
||||
|
||||
case String(keyPath: \Dog.age):
|
||||
XCTAssertTrue(property is ValueContainer<Dog>.Required<Int>)
|
||||
case String(keyPath: \Dog.$age):
|
||||
XCTAssertTrue(property is FieldContainer<Dog>.Stored<Int>)
|
||||
|
||||
case String(keyPath: \Dog.friends):
|
||||
XCTAssertTrue(property is RelationshipContainer<Dog>.ToManyOrdered<Dog>)
|
||||
case String(keyPath: \Dog.$friends):
|
||||
XCTAssertTrue(property is FieldContainer<Dog>.Relationship<[Dog]>)
|
||||
|
||||
case String(keyPath: \Dog.friendedBy):
|
||||
XCTAssertTrue(property is RelationshipContainer<Dog>.ToManyUnordered<Dog>)
|
||||
case String(keyPath: \Dog.$friendedBy):
|
||||
XCTAssertTrue(property is FieldContainer<Dog>.Relationship<Set<Dog>>)
|
||||
|
||||
default:
|
||||
XCTFail("Unknown KeyPath: \"\(property.keyPath)\"")
|
||||
@@ -325,7 +334,7 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
XCTAssertEqual(dog.nickname, "Spot")
|
||||
|
||||
let person = transaction.create(Into<Person>())
|
||||
XCTAssertTrue(person.pets.value.isEmpty)
|
||||
XCTAssertTrue(person.pets.isEmpty)
|
||||
XCTAssertEqual(person.customField.string, "customString")
|
||||
XCTAssertEqual(person.job, .unemployed)
|
||||
|
||||
@@ -382,12 +391,12 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
|
||||
|
||||
|
||||
person.pets.value.insert(dog)
|
||||
person.pets.insert(dog)
|
||||
XCTAssertEqual(person.pets.count, 1)
|
||||
XCTAssertEqual(person.pets.value.first, dog)
|
||||
XCTAssertEqual(person.pets.value.first?.master.value, person)
|
||||
XCTAssertEqual(dog.master.value, person)
|
||||
XCTAssertEqual(dog.master.value?.pets.value.first, dog)
|
||||
XCTAssertEqual(person.pets.first, dog)
|
||||
XCTAssertEqual(person.pets.first?.master, person)
|
||||
XCTAssertEqual(dog.master, person)
|
||||
XCTAssertEqual(dog.master?.pets.first, dog)
|
||||
},
|
||||
success: { _ in
|
||||
|
||||
@@ -424,42 +433,44 @@ class DynamicModelTests: BaseTestDataTestCase {
|
||||
XCTAssertEqual(person!.displayName, "Sir John")
|
||||
XCTAssertEqual(person!.customField.string, "customString")
|
||||
XCTAssertEqual(person!.job, .engineer)
|
||||
XCTAssertEqual(person!.pets.value.first, dog)
|
||||
XCTAssertEqual(person!.pets.first, dog)
|
||||
|
||||
let p3 = Where<Dog>({ $0.age == 10 })
|
||||
let p3 = Where<Dog>({ $0.$age == 10 })
|
||||
XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10))
|
||||
|
||||
let totalAge = try transaction.queryValue(From<Dog>().select(Int.self, .sum(\Dog.age)))
|
||||
let totalAge = try transaction.queryValue(
|
||||
From<Dog>().select(Int.self, .sum(\.$age))
|
||||
)
|
||||
XCTAssertEqual(totalAge, 1)
|
||||
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>()
|
||||
.where(\Animal.$species == "Dog" && \Dog.age == 10)
|
||||
.where(\Animal.$species == "Dog" && \Dog.$age == 10)
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>()
|
||||
.where(\Dog.age == 10 && \Animal.$species == "Dog")
|
||||
.where(\Dog.$age == 10 && \Animal.$species == "Dog")
|
||||
.orderBy(.ascending({ $0.$species }))
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
Where<Dog>({ $0.age > 10 && $0.age <= 15 })
|
||||
Where<Dog>({ $0.$age > 10 && $0.$age <= 15 })
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
Where<Dog>({ $0.$species == "Dog" && $0.age == 10 })
|
||||
Where<Dog>({ $0.$species == "Dog" && $0.$age == 10 })
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
Where<Dog>({ $0.age == 10 && $0.$species == "Dog" })
|
||||
Where<Dog>({ $0.$age == 10 && $0.$species == "Dog" })
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
Where<Dog>({ $0.age > 10 && $0.age <= 15 })
|
||||
Where<Dog>({ $0.$age > 10 && $0.$age <= 15 })
|
||||
)
|
||||
_ = try transaction.fetchAll(
|
||||
From<Dog>(),
|
||||
(\Dog.age > 10 && \Dog.age <= 15)
|
||||
(\Dog.$age > 10 && \Dog.$age <= 15)
|
||||
)
|
||||
},
|
||||
success: { _ in
|
||||
|
||||
@@ -104,18 +104,18 @@ final class WhereTests: XCTestCase {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"master.pets",
|
||||
(\Animal.master ~ \.pets).description,
|
||||
String(keyPath: \Animal.master ~ \.pets)
|
||||
(\Animal.$master ~ \.$pets).description,
|
||||
String(keyPath: \Animal.$master ~ \.$pets)
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"master.pets.species",
|
||||
(\Animal.master ~ \.pets ~ \.$species).description,
|
||||
String(keyPath: \Animal.master ~ \.pets ~ \.$species)
|
||||
(\Animal.$master ~ \.$pets ~ \.$species).description,
|
||||
String(keyPath: \Animal.$master ~ \.$pets ~ \.$species)
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"master.pets.master",
|
||||
(\Animal.master ~ \.pets ~ \.master).description,
|
||||
String(keyPath: \Animal.master ~ \.pets ~ \.master)
|
||||
(\Animal.$master ~ \.$pets ~ \.$master).description,
|
||||
String(keyPath: \Animal.$master ~ \.$pets ~ \.$master)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -138,8 +138,8 @@ final class WhereTests: XCTestCase {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"master.pets.@count",
|
||||
(\Animal.master ~ \.pets).count().description,
|
||||
String(keyPath: (\Animal.master ~ \.pets).count())
|
||||
(\Animal.$master ~ \.$pets).count().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets).count())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -162,13 +162,13 @@ final class WhereTests: XCTestCase {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"ANY master.pets",
|
||||
(\Animal.master ~ \.pets).any().description,
|
||||
String(keyPath: (\Animal.master ~ \.pets).any())
|
||||
(\Animal.$master ~ \.$pets).any().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets).any())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"ANY master.pets.species",
|
||||
(\Animal.master ~ \.pets ~ \.$species).any().description,
|
||||
String(keyPath: (\Animal.master ~ \.pets ~ \.$species).any())
|
||||
(\Animal.$master ~ \.$pets ~ \.$species).any().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets ~ \.$species).any())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -191,13 +191,13 @@ final class WhereTests: XCTestCase {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"ALL master.pets",
|
||||
(\Animal.master ~ \.pets).all().description,
|
||||
String(keyPath: (\Animal.master ~ \.pets).all())
|
||||
(\Animal.$master ~ \.$pets).all().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets).all())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"ALL master.pets.species",
|
||||
(\Animal.master ~ \.pets ~ \.$species).all().description,
|
||||
String(keyPath: (\Animal.master ~ \.pets ~ \.$species).all())
|
||||
(\Animal.$master ~ \.$pets ~ \.$species).all().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets ~ \.$species).all())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -220,13 +220,13 @@ final class WhereTests: XCTestCase {
|
||||
|
||||
XCTAssertAllEqual(
|
||||
"NONE master.pets",
|
||||
(\Animal.master ~ \.pets).none().description,
|
||||
String(keyPath: (\Animal.master ~ \.pets).none())
|
||||
(\Animal.$master ~ \.$pets).none().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets).none())
|
||||
)
|
||||
XCTAssertAllEqual(
|
||||
"NONE master.pets.species",
|
||||
(\Animal.master ~ \.pets ~ \.$species).none().description,
|
||||
String(keyPath: (\Animal.master ~ \.pets ~ \.$species).none())
|
||||
(\Animal.$master ~ \.$pets ~ \.$species).none().description,
|
||||
String(keyPath: (\Animal.$master ~ \.$pets ~ \.$species).none())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -247,7 +247,7 @@ final class WhereTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.master ~ \.$name) == dummy
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$name) == dummy
|
||||
let predicate = NSPredicate(format: "master.name == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
@@ -265,7 +265,7 @@ final class WhereTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.master ~ \.spouse ~ \.$name) == dummy
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$spouse ~ \.$name) == dummy
|
||||
let predicate = NSPredicate(format: "master.spouse.name == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
@@ -283,7 +283,7 @@ final class WhereTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.master ~ \.pets).count() == count
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$pets).count() == count
|
||||
let predicate = NSPredicate(format: "master.pets.@count == %d", count)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
@@ -301,7 +301,7 @@ final class WhereTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.master ~ \.pets ~ \.$species).any() == dummy
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$pets ~ \.$species).any() == dummy
|
||||
let predicate = NSPredicate(format: "ANY master.pets.species == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
@@ -319,7 +319,7 @@ final class WhereTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.master ~ \.pets ~ \.$species).all() == dummy
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$pets ~ \.$species).all() == dummy
|
||||
let predicate = NSPredicate(format: "ALL master.pets.species == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
@@ -337,7 +337,7 @@ final class WhereTests: XCTestCase {
|
||||
}
|
||||
do {
|
||||
|
||||
let whereClause: Where<Animal> = (\.master ~ \.pets ~ \.$species).none() == dummy
|
||||
let whereClause: Where<Animal> = (\.$master ~ \.$pets ~ \.$species).none() == dummy
|
||||
let predicate = NSPredicate(format: "NONE master.pets.species == %@", dummy)
|
||||
XCTAssertAllEqual(whereClause, Where<Animal>(predicate))
|
||||
XCTAssertAllEqual(whereClause.predicate, predicate)
|
||||
|
||||
@@ -339,6 +339,23 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
keyPathsByAffectedKeyPaths[attribute.keyPath] = entityDescriptionValues.affectedByKeyPaths
|
||||
customGetterSetterByKeyPaths[attribute.keyPath] = (attribute.getter, attribute.setter)
|
||||
fieldCoders[attribute.keyPath] = valueTransformer
|
||||
|
||||
case let relationship as FieldRelationshipProtocol:
|
||||
Internals.assert(
|
||||
!NSManagedObject.instancesRespond(to: Selector(relationship.keyPath)),
|
||||
"Relationship Property name \"\(String(reflecting: entity.type)).\(relationship.keyPath)\" is not allowed because it collides with \"\(String(reflecting: NSManagedObject.self)).\(relationship.keyPath)\""
|
||||
)
|
||||
let entityDescriptionValues = relationship.entityDescriptionValues()
|
||||
let description = NSRelationshipDescription()
|
||||
description.name = relationship.keyPath
|
||||
description.minCount = entityDescriptionValues.minCount
|
||||
description.maxCount = entityDescriptionValues.maxCount
|
||||
description.isOrdered = entityDescriptionValues.isOrdered
|
||||
description.deleteRule = entityDescriptionValues.deleteRule
|
||||
description.versionHashModifier = entityDescriptionValues.versionHashModifier
|
||||
description.renamingIdentifier = entityDescriptionValues.renamingIdentifier
|
||||
propertyDescriptions.append(description)
|
||||
keyPathsByAffectedKeyPaths[relationship.keyPath] = entityDescriptionValues.affectedByKeyPaths
|
||||
|
||||
case let attribute as AttributeProtocol:
|
||||
Internals.assert(
|
||||
@@ -437,6 +454,26 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
for property in entityType.metaProperties(includeSuperclasses: false) {
|
||||
|
||||
switch property {
|
||||
|
||||
case let relationship as FieldRelationshipProtocol:
|
||||
let (destinationType, destinationKeyPath) = relationship.entityDescriptionValues().inverse
|
||||
let destinationEntity = findEntity(for: destinationType)
|
||||
let description = relationshipsByName[relationship.keyPath]!
|
||||
description.destinationEntity = entityDescriptionsByEntity[destinationEntity]!
|
||||
|
||||
if let destinationKeyPath = destinationKeyPath {
|
||||
|
||||
let inverseRelationshipDescription = findInverseRelationshipMatching(
|
||||
destinationEntity: destinationEntity,
|
||||
destinationKeyPath: destinationKeyPath
|
||||
)
|
||||
description.inverseRelationship = inverseRelationshipDescription
|
||||
|
||||
inverseRelationshipDescription.inverseRelationship = description
|
||||
inverseRelationshipDescription.destinationEntity = entityDescription
|
||||
|
||||
description.destinationEntity!.properties = description.destinationEntity!.properties
|
||||
}
|
||||
|
||||
case let relationship as RelationshipProtocol:
|
||||
let (destinationType, destinationKeyPath) = relationship.entityDescriptionValues().inverse
|
||||
|
||||
@@ -173,6 +173,9 @@ extension CoreStoreObject {
|
||||
case let property as FieldAttributeProtocol:
|
||||
attributes[property.keyPath] = type(of: property).read(field: property, for: object.rawObject!)
|
||||
|
||||
case let property as FieldRelationshipProtocol:
|
||||
attributes[property.keyPath] = type(of: property).valueForSnapshot(field: property, for: object.rawObject!)
|
||||
|
||||
case let property as AttributeProtocol:
|
||||
attributes[property.keyPath] = property.valueForSnapshot
|
||||
|
||||
|
||||
142
Sources/FIeldRelationshipType.swift
Normal file
142
Sources/FIeldRelationshipType.swift
Normal file
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// FIeldRelationshipType.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2020 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - FieldRelationshipType
|
||||
|
||||
public protocol FieldRelationshipType {
|
||||
|
||||
associatedtype DestinationObjectType: CoreStoreObject
|
||||
|
||||
associatedtype NativeValueType: AnyObject
|
||||
|
||||
associatedtype SnapshotValueType
|
||||
|
||||
static func cs_toReturnType(from value: NativeValueType?) -> Self
|
||||
|
||||
static func cs_toNativeType(from value: Self) -> NativeValueType?
|
||||
|
||||
static func cs_valueForSnapshot(from value: NativeValueType?) -> SnapshotValueType
|
||||
}
|
||||
|
||||
public protocol FieldRelationshipToOneType: FieldRelationshipType {}
|
||||
|
||||
|
||||
public protocol FieldRelationshipToManyType: FieldRelationshipType where Self: Sequence {}
|
||||
public protocol FieldRelationshipToManyOrderedType: FieldRelationshipToManyType {}
|
||||
public protocol FieldRelationshipToManyUnorderedType: FieldRelationshipToManyType {}
|
||||
|
||||
|
||||
extension Optional: FieldRelationshipType, FieldRelationshipToOneType where Wrapped: CoreStoreObject {
|
||||
|
||||
public typealias DestinationObjectType = Wrapped
|
||||
|
||||
public typealias NativeValueType = NSManagedObject
|
||||
|
||||
public typealias SnapshotValueType = NSManagedObjectID?
|
||||
|
||||
public static func cs_toReturnType(from value: NativeValueType?) -> Self {
|
||||
|
||||
return value.map(Wrapped.cs_fromRaw(object:))
|
||||
}
|
||||
|
||||
public static func cs_toNativeType(from value: Self) -> NativeValueType? {
|
||||
|
||||
return value?.cs_toRaw()
|
||||
}
|
||||
|
||||
public static func cs_valueForSnapshot(from value: NativeValueType?) -> SnapshotValueType {
|
||||
|
||||
return value?.objectID
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Array: FieldRelationshipType, FieldRelationshipToManyType, FieldRelationshipToManyOrderedType where Element: CoreStoreObject {
|
||||
|
||||
public typealias DestinationObjectType = Element
|
||||
|
||||
public typealias NativeValueType = NSOrderedSet
|
||||
|
||||
public typealias SnapshotValueType = [NSManagedObjectID]
|
||||
|
||||
public static func cs_toReturnType(from value: NativeValueType?) -> Self {
|
||||
|
||||
guard let value = value else {
|
||||
|
||||
return []
|
||||
}
|
||||
return value.map({ Element.cs_fromRaw(object: $0 as! NSManagedObject) })
|
||||
}
|
||||
|
||||
public static func cs_toNativeType(from value: Self) -> NativeValueType? {
|
||||
|
||||
return NSOrderedSet(array: value.map({ $0.rawObject! }))
|
||||
}
|
||||
|
||||
public static func cs_valueForSnapshot(from value: NativeValueType?) -> SnapshotValueType {
|
||||
|
||||
guard let value = value else {
|
||||
|
||||
return []
|
||||
}
|
||||
return value.map({ ($0 as! NSManagedObject).objectID })
|
||||
}
|
||||
}
|
||||
|
||||
extension Set: FieldRelationshipType, FieldRelationshipToManyType, FieldRelationshipToManyUnorderedType where Element: CoreStoreObject {
|
||||
|
||||
public typealias DestinationObjectType = Element
|
||||
|
||||
public typealias NativeValueType = NSSet
|
||||
|
||||
public typealias SnapshotValueType = Set<NSManagedObjectID>
|
||||
|
||||
public static func cs_toReturnType(from value: NativeValueType?) -> Self {
|
||||
|
||||
guard let value = value else {
|
||||
|
||||
return []
|
||||
}
|
||||
return Set(value.map({ Element.cs_fromRaw(object: $0 as! NSManagedObject) }))
|
||||
}
|
||||
|
||||
public static func cs_toNativeType(from value: Self) -> NativeValueType? {
|
||||
|
||||
return NSSet(array: value.map({ $0.rawObject! }))
|
||||
}
|
||||
|
||||
public static func cs_valueForSnapshot(from value: NativeValueType?) -> SnapshotValueType {
|
||||
|
||||
guard let value = value else {
|
||||
|
||||
return []
|
||||
}
|
||||
return .init(value.map({ ($0 as! NSManagedObject).objectID }))
|
||||
}
|
||||
}
|
||||
346
Sources/Field.Relationship.swift
Normal file
346
Sources/Field.Relationship.swift
Normal file
@@ -0,0 +1,346 @@
|
||||
//
|
||||
// Field.ToOne.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2020 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - FieldContainer
|
||||
|
||||
extension FieldContainer {
|
||||
|
||||
// MARK: - Relationship
|
||||
|
||||
@propertyWrapper
|
||||
// @dynamicMemberLookup
|
||||
public struct Relationship<V: FieldRelationshipType>: RelationshipKeyPathStringConvertible, FieldRelationshipProtocol {
|
||||
|
||||
public typealias DeleteRule = RelationshipContainer<O>.DeleteRule
|
||||
|
||||
|
||||
// MARK: @propertyWrapper
|
||||
|
||||
@available(*, unavailable)
|
||||
public var wrappedValue: V {
|
||||
|
||||
get { fatalError() }
|
||||
set { fatalError() }
|
||||
}
|
||||
|
||||
public var projectedValue: Self {
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
public static subscript(
|
||||
_enclosingInstance instance: O,
|
||||
wrapped wrappedKeyPath: ReferenceWritableKeyPath<O, V>,
|
||||
storage storageKeyPath: ReferenceWritableKeyPath<O, Self>
|
||||
) -> V {
|
||||
|
||||
get {
|
||||
|
||||
Internals.assert(
|
||||
instance.rawObject != nil,
|
||||
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return self.read(field: instance[keyPath: storageKeyPath], for: instance.rawObject!) as! V
|
||||
}
|
||||
set {
|
||||
|
||||
Internals.assert(
|
||||
instance.rawObject != nil,
|
||||
"Attempted to access values from a \(Internals.typeName(O.self)) meta object. Meta objects are only used for querying keyPaths and infering types."
|
||||
)
|
||||
return self.modify(field: instance[keyPath: storageKeyPath], for: instance.rawObject!, newValue: newValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: AnyKeyPathStringConvertible
|
||||
|
||||
public var cs_keyPathString: String {
|
||||
|
||||
return self.keyPath
|
||||
}
|
||||
|
||||
|
||||
// MARK: KeyPathStringConvertible
|
||||
|
||||
public typealias ObjectType = O
|
||||
public typealias DestinationValueType = V.DestinationObjectType
|
||||
|
||||
|
||||
// MARK: RelationshipKeyPathStringConvertible
|
||||
|
||||
public typealias ReturnValueType = V
|
||||
|
||||
|
||||
// MARK: PropertyProtocol
|
||||
|
||||
internal let keyPath: KeyPathString
|
||||
|
||||
|
||||
// MARK: FieldProtocol
|
||||
|
||||
internal static func read(field: FieldProtocol, for rawObject: CoreStoreManagedObject) -> Any? {
|
||||
|
||||
Internals.assert(
|
||||
rawObject.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
let field = field as! Self
|
||||
let keyPath = field.keyPath
|
||||
return V.cs_toReturnType(
|
||||
from: rawObject.value(forKey: keyPath) as! V.NativeValueType?
|
||||
)
|
||||
}
|
||||
|
||||
internal static func modify(field: FieldProtocol, for rawObject: CoreStoreManagedObject, newValue: Any?) {
|
||||
|
||||
Internals.assert(
|
||||
rawObject.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
Internals.assert(
|
||||
rawObject.isEditableInContext() == true,
|
||||
"Attempted to update a \(Internals.typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
let newValue = newValue as! V
|
||||
let field = field as! Self
|
||||
let keyPath = field.keyPath
|
||||
return rawObject.setValue(
|
||||
V.cs_toNativeType(from: newValue),
|
||||
forKey: keyPath
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: FieldRelationshipProtocol
|
||||
|
||||
internal let entityDescriptionValues: () -> FieldRelationshipProtocol.EntityDescriptionValues
|
||||
|
||||
internal static func valueForSnapshot(field: FieldProtocol, for rawObject: CoreStoreManagedObject) -> Any? {
|
||||
|
||||
Internals.assert(
|
||||
rawObject.isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(Internals.typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
let field = field as! Self
|
||||
let keyPath = field.keyPath
|
||||
return V.cs_valueForSnapshot(
|
||||
from: rawObject.value(forKey: keyPath) as! V.NativeValueType?
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// MARK: FilePrivate
|
||||
|
||||
fileprivate init(
|
||||
keyPath: KeyPathString,
|
||||
isToMany: Bool,
|
||||
isOrdered: Bool,
|
||||
deleteRule: DeleteRule,
|
||||
inverseKeyPath: @escaping () -> KeyPathString?,
|
||||
versionHashModifier: @escaping () -> String?,
|
||||
renamingIdentifier: @escaping () -> String?,
|
||||
affectedByKeyPaths: @escaping () -> Set<KeyPathString>,
|
||||
minCount: Int,
|
||||
maxCount: Int
|
||||
) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.entityDescriptionValues = {
|
||||
|
||||
let range = (Swift.max(0, minCount) ... maxCount)
|
||||
return (
|
||||
isToMany: isToMany,
|
||||
isOrdered: isOrdered,
|
||||
deleteRule: deleteRule.nativeValue,
|
||||
inverse: (type: V.DestinationObjectType.self, keyPath: inverseKeyPath()),
|
||||
versionHashModifier: versionHashModifier(),
|
||||
renamingIdentifier: renamingIdentifier(),
|
||||
affectedByKeyPaths: affectedByKeyPaths(),
|
||||
minCount: range.lowerBound,
|
||||
maxCount: range.upperBound
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FieldContainer.Relationship where V: FieldRelationshipToOneType {
|
||||
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||
) {
|
||||
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
isToMany: false,
|
||||
isOrdered: false,
|
||||
deleteRule: deleteRule,
|
||||
inverseKeyPath: { nil },
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
affectedByKeyPaths: affectedByKeyPaths,
|
||||
minCount: 0,
|
||||
maxCount: 1
|
||||
)
|
||||
}
|
||||
|
||||
public init<D>(
|
||||
_ keyPath: KeyPathString,
|
||||
inverse: KeyPath<V.DestinationObjectType, FieldContainer<V.DestinationObjectType>.Relationship<D>>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||
) where D: FieldRelationshipType {
|
||||
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
isToMany: false,
|
||||
isOrdered: false,
|
||||
deleteRule: deleteRule,
|
||||
inverseKeyPath: { V.DestinationObjectType.meta[keyPath: inverse].keyPath },
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
affectedByKeyPaths: affectedByKeyPaths,
|
||||
minCount: 0,
|
||||
maxCount: 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension FieldContainer.Relationship: ToManyRelationshipKeyPathStringConvertible where V: FieldRelationshipToManyType {}
|
||||
|
||||
extension FieldContainer.Relationship where V: FieldRelationshipToManyOrderedType {
|
||||
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||
) {
|
||||
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
isToMany: true,
|
||||
isOrdered: true,
|
||||
deleteRule: deleteRule,
|
||||
inverseKeyPath: { nil },
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
affectedByKeyPaths: affectedByKeyPaths,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount
|
||||
)
|
||||
}
|
||||
|
||||
public init<D>(
|
||||
_ keyPath: KeyPathString,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
inverse: @escaping (V.DestinationObjectType) -> FieldContainer<V.DestinationObjectType>.Relationship<D>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||
) where D: FieldRelationshipType {
|
||||
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
isToMany: true,
|
||||
isOrdered: true,
|
||||
deleteRule: deleteRule,
|
||||
inverseKeyPath: { inverse(V.DestinationObjectType.meta).keyPath },
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
affectedByKeyPaths: affectedByKeyPaths,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension FieldContainer.Relationship where V: FieldRelationshipToManyUnorderedType {
|
||||
|
||||
public init(
|
||||
_ keyPath: KeyPathString,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||
) {
|
||||
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
isToMany: true,
|
||||
isOrdered: false,
|
||||
deleteRule: deleteRule,
|
||||
inverseKeyPath: { nil },
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
affectedByKeyPaths: affectedByKeyPaths,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount
|
||||
)
|
||||
}
|
||||
|
||||
public init<D>(
|
||||
_ keyPath: KeyPathString,
|
||||
minCount: Int = 0,
|
||||
maxCount: Int = 0,
|
||||
inverse: @escaping (V.DestinationObjectType) -> FieldContainer<V.DestinationObjectType>.Relationship<D>,
|
||||
deleteRule: DeleteRule = .nullify,
|
||||
versionHashModifier: @autoclosure @escaping () -> String? = nil,
|
||||
previousVersionKeyPath: @autoclosure @escaping () -> String? = nil,
|
||||
affectedByKeyPaths: @autoclosure @escaping () -> Set<KeyPathString> = []
|
||||
) where D: FieldRelationshipType {
|
||||
|
||||
self.init(
|
||||
keyPath: keyPath,
|
||||
isToMany: true,
|
||||
isOrdered: false,
|
||||
deleteRule: deleteRule,
|
||||
inverseKeyPath: { inverse(V.DestinationObjectType.meta).keyPath },
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: previousVersionKeyPath,
|
||||
affectedByKeyPaths: affectedByKeyPaths,
|
||||
minCount: minCount,
|
||||
maxCount: maxCount
|
||||
)
|
||||
}
|
||||
}
|
||||
49
Sources/FieldRelationshipProtocol.swift
Normal file
49
Sources/FieldRelationshipProtocol.swift
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// FieldRelationshipProtocol.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2020 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - FieldRelationshipProtocol
|
||||
|
||||
internal protocol FieldRelationshipProtocol: FieldProtocol {
|
||||
|
||||
typealias EntityDescriptionValues = (
|
||||
isToMany: Bool,
|
||||
isOrdered: Bool,
|
||||
deleteRule: NSDeleteRule,
|
||||
inverse: (type: CoreStoreObject.Type, KeyPathString?),
|
||||
versionHashModifier: String?,
|
||||
renamingIdentifier: String?,
|
||||
affectedByKeyPaths: Set<KeyPathString>,
|
||||
minCount: Int,
|
||||
maxCount: Int
|
||||
)
|
||||
|
||||
var entityDescriptionValues: () -> EntityDescriptionValues { get }
|
||||
|
||||
static func valueForSnapshot(field: FieldProtocol, for rawObject: CoreStoreManagedObject) -> Any?
|
||||
}
|
||||
@@ -447,6 +447,17 @@ public func ~= <O, V, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, ValueCon
|
||||
|
||||
// MARK: - KeyPath where Root: CoreStoreObject, Value: ValueContainer<Root>.Required<QueryableAttributeType & Comparable>
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.$age < 20))
|
||||
```
|
||||
*/
|
||||
public func < <O, V: Comparable>(_ keyPath: KeyPath<O, FieldContainer<O>.Stored<V>>, _ value: V) -> Where<O> {
|
||||
|
||||
return Where<O>("%K < %@", O.meta[keyPath: keyPath].keyPath, value.cs_toFieldStoredNativeType() as! V.FieldStoredNativeType)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
@@ -458,6 +469,17 @@ public func < <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Require
|
||||
return Where<O>("%K < %@", O.meta[keyPath: keyPath].keyPath, value.cs_toQueryableNativeType())
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.$age > 20))
|
||||
```
|
||||
*/
|
||||
public func > <O, V: Comparable>(_ keyPath: KeyPath<O, FieldContainer<O>.Stored<V>>, _ value: V) -> Where<O> {
|
||||
|
||||
return Where<O>("%K > %@", O.meta[keyPath: keyPath].keyPath, value.cs_toFieldStoredNativeType() as! V.FieldStoredNativeType)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
@@ -469,6 +491,17 @@ public func > <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Require
|
||||
return Where<O>("%K > %@", O.meta[keyPath: keyPath].keyPath, value.cs_toQueryableNativeType())
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.$age <= 20))
|
||||
```
|
||||
*/
|
||||
public func <= <O, V: Comparable>(_ keyPath: KeyPath<O, FieldContainer<O>.Stored<V>>, _ value: V) -> Where<O> {
|
||||
|
||||
return Where<O>("%K <= %@", O.meta[keyPath: keyPath].keyPath, value.cs_toFieldStoredNativeType() as! V.FieldStoredNativeType)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
@@ -480,6 +513,17 @@ public func <= <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Requir
|
||||
return Where<O>("%K <= %@", O.meta[keyPath: keyPath].keyPath, value.cs_toQueryableNativeType())
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.$age >= 20))
|
||||
```
|
||||
*/
|
||||
public func >= <O, V: Comparable>(_ keyPath: KeyPath<O, FieldContainer<O>.Stored<V>>, _ value: V) -> Where<O> {
|
||||
|
||||
return Where<O>("%K >= %@", O.meta[keyPath: keyPath].keyPath, value.cs_toFieldStoredNativeType() as! V.FieldStoredNativeType)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
@@ -494,6 +538,17 @@ public func >= <O, V: Comparable>(_ keyPath: KeyPath<O, ValueContainer<O>.Requir
|
||||
|
||||
// MARK: - KeyPath where Root: CoreStoreObject, Value: ValueContainer<Root>.Optional<QueryableAttributeType & Comparable>
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.$age < 20))
|
||||
```
|
||||
*/
|
||||
public func < <O, V: FieldOptionalType>(_ keyPath: KeyPath<O, FieldContainer<O>.Stored<V>>, _ value: V) -> Where<O> where V.Wrapped: Comparable {
|
||||
|
||||
return Where<O>("%K < %@", O.meta[keyPath: keyPath].keyPath, value.cs_toFieldStoredNativeType() as! V.FieldStoredNativeType)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than a value
|
||||
```
|
||||
@@ -512,6 +567,17 @@ public func < <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ val
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.$age > 20))
|
||||
```
|
||||
*/
|
||||
public func > <O, V: FieldOptionalType>(_ keyPath: KeyPath<O, FieldContainer<O>.Stored<V>>, _ value: V) -> Where<O> where V.Wrapped: Comparable {
|
||||
|
||||
return Where<O>("%K > %@", O.meta[keyPath: keyPath].keyPath, value.cs_toFieldStoredNativeType() as! V.FieldStoredNativeType)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than a value
|
||||
```
|
||||
@@ -530,6 +596,17 @@ public func > <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ val
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.$age <= 20))
|
||||
```
|
||||
*/
|
||||
public func <= <O, V: FieldOptionalType>(_ keyPath: KeyPath<O, FieldContainer<O>.Stored<V>>, _ value: V) -> Where<O> where V.Wrapped: Comparable {
|
||||
|
||||
return Where<O>("%K <= %@", O.meta[keyPath: keyPath].keyPath, value.cs_toFieldStoredNativeType() as! V.FieldStoredNativeType)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is less than or equal to a value
|
||||
```
|
||||
@@ -548,6 +625,17 @@ public func <= <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ va
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
let person = dataStack.fetchOne(From<Person>().where(\.$age >= 20))
|
||||
```
|
||||
*/
|
||||
public func >= <O, V: FieldOptionalType>(_ keyPath: KeyPath<O, FieldContainer<O>.Stored<V>>, _ value: V) -> Where<O> where V.Wrapped: Comparable {
|
||||
|
||||
return Where<O>("%K >= %@", O.meta[keyPath: keyPath].keyPath, value.cs_toFieldStoredNativeType() as! V.FieldStoredNativeType)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is greater than or equal to a value
|
||||
```
|
||||
@@ -569,6 +657,17 @@ public func >= <O, V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, _ va
|
||||
|
||||
// MARK: - KeyPath where Root: CoreStoreObject, Value: RelationshipContainer<Root>.ToOne<CoreStoreObject>
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.$master == john))
|
||||
```
|
||||
*/
|
||||
public func == <O, D: FieldRelationshipToOneType>(_ keyPath: KeyPath<O, FieldContainer<O>.Relationship<D>>, _ object: D.DestinationObjectType?) -> Where<O> {
|
||||
|
||||
return Where<O>(O.meta[keyPath: keyPath].keyPath, isEqualTo: object)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is equal to a value
|
||||
```
|
||||
@@ -591,6 +690,17 @@ public func == <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>,
|
||||
return Where<O>(O.meta[keyPath: keyPath].keyPath, isEqualTo: object)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
let dog = dataStack.fetchOne(From<Dog>().where(\.$master != john))
|
||||
```
|
||||
*/
|
||||
public func != <O, D: FieldRelationshipToOneType>(_ keyPath: KeyPath<O, FieldContainer<O>.Relationship<D>>, _ object: D.DestinationObjectType?) -> Where<O> {
|
||||
|
||||
return !Where<O>(O.meta[keyPath: keyPath].keyPath, isEqualTo: object)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by comparing if a property is not equal to a value
|
||||
```
|
||||
@@ -613,6 +723,17 @@ public func != <O, D>(_ keyPath: KeyPath<O, RelationshipContainer<O>.ToOne<D>>,
|
||||
return !Where<O>(O.meta[keyPath: keyPath].keyPath, isEqualTo: object)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains a value of a property
|
||||
```
|
||||
let dog = dataStack.fetchOne(From<Dog>().where([john, bob, joe] ~= \.$master))
|
||||
```
|
||||
*/
|
||||
public func ~= <O, D: FieldRelationshipToOneType, S: Sequence>(_ sequence: S, _ keyPath: KeyPath<O, FieldContainer<O>.Relationship<D>>) -> Where<O> where S.Iterator.Element == D.DestinationObjectType {
|
||||
|
||||
return Where<O>(O.meta[keyPath: keyPath].keyPath, isMemberOf: sequence)
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a `Where` clause by checking if a sequence contains a value of a property
|
||||
```
|
||||
|
||||
@@ -379,45 +379,15 @@ extension SelectTerm where O: NSManagedObject {
|
||||
// MARK: - SelectTerm where O: CoreStoreObject
|
||||
|
||||
extension SelectTerm where O: CoreStoreObject {
|
||||
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying an entity attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>) -> SelectTerm<O> {
|
||||
|
||||
return self.attribute(O.meta[keyPath: keyPath].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying an entity attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>) -> SelectTerm<O> {
|
||||
|
||||
return self.attribute(O.meta[keyPath: keyPath].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying an entity attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<V>>) -> SelectTerm<O> {
|
||||
|
||||
return self.attribute(O.meta[keyPath: keyPath].keyPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying an entity attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
|
||||
*/
|
||||
public static func attribute<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<V>>) -> SelectTerm<O> {
|
||||
|
||||
return self.attribute(O.meta[keyPath: keyPath].keyPath)
|
||||
public static func attribute<K: AttributeKeyPathStringConvertible>(_ keyPath: KeyPath<O, K>) -> SelectTerm<O> where K.ObjectType == O {
|
||||
|
||||
return self.attribute(O.meta[keyPath: keyPath].cs_keyPathString)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,42 +396,9 @@ extension SelectTerm where O: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
public static func average<K: AttributeKeyPathStringConvertible>(_ keyPath: KeyPath<O, K>, as alias: KeyPathString? = nil) -> SelectTerm<O> where K.ObjectType == O{
|
||||
|
||||
return self.average(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.average(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.average(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "average(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
|
||||
*/
|
||||
public static func average<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.average(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.average(O.meta[keyPath: keyPath].cs_keyPathString, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -470,46 +407,10 @@ extension SelectTerm where O: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count<V>(_ keyPath: KeyPath<O,
|
||||
ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
public static func count<K: AttributeKeyPathStringConvertible>(_ keyPath: KeyPath<O,
|
||||
K>, as alias: KeyPathString? = nil) -> SelectTerm<O> where K.ObjectType == O {
|
||||
|
||||
return self.count(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for a count query.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count<V>(_ keyPath: KeyPath<O,
|
||||
ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.count(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for a count query.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count<V>(_ keyPath: KeyPath<O,
|
||||
TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.count(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for a count query.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "count(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for a count query
|
||||
*/
|
||||
public static func count<V>(_ keyPath: KeyPath<O,
|
||||
TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.count(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.count(O.meta[keyPath: keyPath].cs_keyPathString, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -518,46 +419,10 @@ extension SelectTerm where O: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum<V>(_ keyPath: KeyPath<O,
|
||||
ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
public static func maximum<K: AttributeKeyPathStringConvertible>(_ keyPath: KeyPath<O,
|
||||
K>, as alias: KeyPathString? = nil) -> SelectTerm<O> where K.ObjectType == O {
|
||||
|
||||
return self.maximum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum<V>(_ keyPath: KeyPath<O,
|
||||
ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.maximum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum<V>(_ keyPath: KeyPath<O,
|
||||
TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.maximum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
|
||||
*/
|
||||
public static func maximum<V>(_ keyPath: KeyPath<O,
|
||||
TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.maximum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.maximum(O.meta[keyPath: keyPath].cs_keyPathString, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -566,42 +431,9 @@ extension SelectTerm where O: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
public static func minimum<K: AttributeKeyPathStringConvertible>(_ keyPath: KeyPath<O, K>, as alias: KeyPathString? = nil) -> SelectTerm<O> where K.ObjectType == O {
|
||||
|
||||
return self.minimum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.minimum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.minimum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "min(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
|
||||
*/
|
||||
public static func minimum<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.minimum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.minimum(O.meta[keyPath: keyPath].cs_keyPathString, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -610,42 +442,9 @@ extension SelectTerm where O: CoreStoreObject {
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
public static func sum<K: AttributeKeyPathStringConvertible>(_ keyPath: KeyPath<O, K>, as alias: KeyPathString? = nil) -> SelectTerm<O> where K.ObjectType == O {
|
||||
|
||||
return self.sum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum<V>(_ keyPath: KeyPath<O, ValueContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.sum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Required<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.sum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
}
|
||||
|
||||
/**
|
||||
Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute.
|
||||
- parameter keyPath: the attribute name
|
||||
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "sum(<attributeName>)" is used
|
||||
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
|
||||
*/
|
||||
public static func sum<V>(_ keyPath: KeyPath<O, TransformableContainer<O>.Optional<V>>, as alias: KeyPathString? = nil) -> SelectTerm<O> {
|
||||
|
||||
return self.sum(O.meta[keyPath: keyPath].keyPath, as: alias)
|
||||
return self.sum(O.meta[keyPath: keyPath].cs_keyPathString, as: alias)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -235,6 +235,20 @@ public func ~ <O: NSManagedObject, D: NSManagedObject, T, C: AllowedObjectiveCTo
|
||||
|
||||
// MARK: - ~ where D: CoreStoreObject
|
||||
|
||||
/**
|
||||
Connects multiple `KeyPathStringConvertible`s to create a type-safe chain usable in query/fetch expressions
|
||||
```
|
||||
let owner = dataStack.fetchOne(From<Pet>().where((\.$master ~ \.$name) == "John"))
|
||||
```
|
||||
*/
|
||||
public func ~ <O: CoreStoreObject, D: FieldRelationshipToOneType, K: KeyPathStringConvertible>(_ lhs: KeyPath<O, FieldContainer<O>.Relationship<D>>, _ rhs: KeyPath<D.DestinationObjectType, K>) -> Where<O>.Expression<Where<O>.SingleTarget, K.DestinationValueType> where K.ObjectType == D.DestinationObjectType {
|
||||
|
||||
return .init(
|
||||
O.meta[keyPath: lhs].cs_keyPathString,
|
||||
D.DestinationObjectType.meta[keyPath: rhs].cs_keyPathString
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Connects multiple `KeyPathStringConvertible`s to create a type-safe chain usable in query/fetch expressions
|
||||
```
|
||||
@@ -277,6 +291,20 @@ public func ~ <O: CoreStoreObject, D: CoreStoreObject, T, K: KeyPathStringConver
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Connects multiple `KeyPathStringConvertible`s to create a type-safe chain usable in query/fetch expressions
|
||||
```
|
||||
let happyPets = dataStack.fetchAll(From<Pet>().where((\.$master ~ \.$pets).count() > 1))
|
||||
```
|
||||
*/
|
||||
public func ~ <O: CoreStoreObject, D: FieldRelationshipToOneType, K: ToManyRelationshipKeyPathStringConvertible>(_ lhs: KeyPath<O, FieldContainer<O>.Relationship<D>>, _ rhs: KeyPath<D.DestinationObjectType, K>) -> Where<O>.Expression<Where<O>.CollectionTarget, K.DestinationValueType> where K.ObjectType == D.DestinationObjectType {
|
||||
|
||||
return .init(
|
||||
O.meta[keyPath: lhs].cs_keyPathString,
|
||||
D.DestinationObjectType.meta[keyPath: rhs].cs_keyPathString
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Connects multiple `KeyPathStringConvertible`s to create a type-safe chain usable in query/fetch expressions
|
||||
```
|
||||
|
||||
@@ -451,6 +451,17 @@ extension Where where O: CoreStoreObject {
|
||||
|
||||
self.init(O.meta[keyPath: keyPath].keyPath, isEqualTo: value)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a `Where` clause that compares equality
|
||||
|
||||
- parameter keyPath: the keyPath to compare with
|
||||
- parameter value: the arguments for the `==` operator
|
||||
*/
|
||||
public init<V: FieldRelationshipToOneType>(_ keyPath: KeyPath<O, FieldContainer<O>.Relationship<V>>, isEqualTo value: V.DestinationObjectType?) {
|
||||
|
||||
self.init(O.meta[keyPath: keyPath].keyPath, isEqualTo: value)
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes a `Where` clause that compares equality to `nil`
|
||||
|
||||
Reference in New Issue
Block a user