Field.Relationship propertyWrapper

This commit is contained in:
John Estropia
2020-01-20 17:13:01 +09:00
parent bcc2d9def3
commit 92ad895044
12 changed files with 881 additions and 304 deletions

View File

@@ -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

View File

@@ -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

View 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 }))
}
}

View 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
)
}
}

View 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?
}

View File

@@ -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
```

View File

@@ -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)
}
}

View File

@@ -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
```

View File

@@ -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`