Swift 5 support. WIP: Support type-safe predicate expressions

This commit is contained in:
John Estropia
2019-02-01 18:32:22 +09:00
parent 41902fee2e
commit db4426e6b9
15 changed files with 715 additions and 73 deletions

View File

@@ -578,6 +578,8 @@
B5D7A5B81CA3BF8F005C752B /* CSInto.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D7A5B51CA3BF8F005C752B /* CSInto.swift */; };
B5D7A5B91CA3BF8F005C752B /* CSInto.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D7A5B51CA3BF8F005C752B /* CSInto.swift */; };
B5D7A5BA1CA3BF8F005C752B /* CSInto.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D7A5B51CA3BF8F005C752B /* CSInto.swift */; };
B5DAFB482203D9F8003FCCD0 /* Where.Expression.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DAFB472203D9F8003FCCD0 /* Where.Expression.swift */; };
B5DAFB4A2203E01D003FCCD0 /* KeyPathGenericBindings.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DAFB492203E01D003FCCD0 /* KeyPathGenericBindings.swift */; };
B5DBE2CD1C9914A900B5CEFA /* CSCoreStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DBE2CC1C9914A900B5CEFA /* CSCoreStore.swift */; };
B5DBE2CE1C9914A900B5CEFA /* CSCoreStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DBE2CC1C9914A900B5CEFA /* CSCoreStore.swift */; };
B5DBE2CF1C9914A900B5CEFA /* CSCoreStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DBE2CC1C9914A900B5CEFA /* CSCoreStore.swift */; };
@@ -902,6 +904,8 @@
B5D3F6441C887C0A00C7492A /* LegacySQLiteStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacySQLiteStore.swift; sourceTree = "<group>"; };
B5D7A5B51CA3BF8F005C752B /* CSInto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSInto.swift; sourceTree = "<group>"; };
B5D9C8F61B160ED200E64F0E /* CoreStore.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; path = CoreStore.podspec; sourceTree = SOURCE_ROOT; };
B5DAFB472203D9F8003FCCD0 /* Where.Expression.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Where.Expression.swift; sourceTree = "<group>"; };
B5DAFB492203E01D003FCCD0 /* KeyPathGenericBindings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPathGenericBindings.swift; sourceTree = "<group>"; };
B5DBE2CC1C9914A900B5CEFA /* CSCoreStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSCoreStore.swift; sourceTree = "<group>"; };
B5DBE2D11C991B3E00B5CEFA /* CSDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSDataStack.swift; sourceTree = "<group>"; };
B5DBE2DA1C9939E100B5CEFA /* CoreStoreTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CoreStoreTests-Bridging-Header.h"; sourceTree = "<group>"; };
@@ -1333,6 +1337,7 @@
B5A1DAC71F111BFA003CF369 /* KeyPath+Querying.swift */,
B5D339EB1E9495E500C880DE /* CoreStoreObject+Querying.swift */,
B5CA2B111F81DBFE004B1936 /* DynamicKeyPath.swift */,
B5DAFB492203E01D003FCCD0 /* KeyPathGenericBindings.swift */,
);
name = "KeyPath Utilities";
sourceTree = "<group>";
@@ -1480,6 +1485,7 @@
B5E84F011AFF847B0064E85B /* From.swift */,
B5E84F031AFF847B0064E85B /* Select.swift */,
B5E84F051AFF847B0064E85B /* Where.swift */,
B5DAFB472203D9F8003FCCD0 /* Where.Expression.swift */,
B5E84F041AFF847B0064E85B /* OrderBy.swift */,
B5E84F021AFF847B0064E85B /* GroupBy.swift */,
B5E84F001AFF847B0064E85B /* Tweak.swift */,
@@ -1896,6 +1902,7 @@
B5ECDBFF1CA80CBA00C7F112 /* CSWhere.swift in Sources */,
B5ECDC051CA8138100C7F112 /* CSOrderBy.swift in Sources */,
B5E1B5981CAA0C23007FD580 /* CSObjectObserver.swift in Sources */,
B5DAFB4A2203E01D003FCCD0 /* KeyPathGenericBindings.swift in Sources */,
B5519A5F1CA21954002BEF78 /* CSAsynchronousDataTransaction.swift in Sources */,
B52FD3AA1E3B3EF10001D919 /* NSManagedObject+Logging.swift in Sources */,
B52F74411E9B8724005F3DAC /* UnsafeDataModelSchema.swift in Sources */,
@@ -1935,6 +1942,7 @@
B56007111B3F6BD500A9A8F9 /* Into.swift in Sources */,
B5E84F111AFF847B0064E85B /* Select.swift in Sources */,
B51260931E9B28F100402229 /* EntityIdentifier.swift in Sources */,
B5DAFB482203D9F8003FCCD0 /* Where.Expression.swift in Sources */,
B5FE4DA21C8481E100FA6A91 /* StorageInterface.swift in Sources */,
B53FB9FE1CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */,
B5DBE2D21C991B3E00B5CEFA /* CSDataStack.swift in Sources */,
@@ -2709,7 +2717,7 @@
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.2;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TVOS_DEPLOYMENT_TARGET = 10.0;
VERSIONING_SYSTEM = "apple-generic";
@@ -2771,7 +2779,7 @@
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.2;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TVOS_DEPLOYMENT_TARGET = 10.0;
VALIDATE_PRODUCT = YES;

View File

@@ -30,9 +30,10 @@ import CoreStore
#if os(macOS)
typealias Color = NSColor
#else
typealias Color = UIColor
#endif
class Animal: CoreStoreObject {
@@ -134,9 +135,11 @@ class DynamicModelTests: BaseTestDataTestCase {
let k1 = String(keyPath: \Animal.species)
XCTAssertEqual(k1, "species")
#if swift(<5.0)
let k2 = String(keyPath: \Dog.species)
XCTAssertEqual(k2, "species")
#endif
let k3 = String(keyPath: \Dog.nickname)
XCTAssertEqual(k3, "nickname")
@@ -272,11 +275,11 @@ class DynamicModelTests: BaseTestDataTestCase {
_ = try transaction.fetchAll(
From<Dog>()
.where(\Animal.species == "Dog" && \.age == 10)
.where(\Animal.species == "Dog" && \Dog.age == 10)
)
_ = try transaction.fetchAll(
From<Dog>()
.where(\.age == 10 && \Animal.species == "Dog")
.where(\Dog.age == 10 && \Animal.species == "Dog")
.orderBy(.ascending({ $0.species }))
)
_ = try transaction.fetchAll(
@@ -317,7 +320,9 @@ class DynamicModelTests: BaseTestDataTestCase {
dynamic func test_ThatDynamicModelKeyPaths_CanBeCreated() {
XCTAssertEqual(String(keyPath: \Animal.species), "species")
#if swift(<5.0)
XCTAssertEqual(String(keyPath: \Dog.species), "species")
#endif
}
@nonobjc

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="11759" systemVersion="16C67" minimumToolsVersion="Xcode 4.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14460.32" systemVersion="17G2307" minimumToolsVersion="Xcode 4.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="TestEntity1AAA" representedClassName="CoreStoreTests.TestEntity1" syncable="YES">
<attribute name="testBoolean" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testData" optional="YES" attributeType="Binary" syncable="YES"/>
@@ -9,6 +9,8 @@
<attribute name="testNil" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="testNumber" optional="YES" attributeType="Integer 32" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testString" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="testToManyUnordered" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TestEntity1AAA" inverseName="testToOne" inverseEntity="TestEntity1AAA" syncable="YES"/>
<relationship name="testToOne" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TestEntity1AAA" inverseName="testToManyUnordered" inverseEntity="TestEntity1AAA" syncable="YES"/>
</entity>
<entity name="TestEntity2" representedClassName="CoreStoreTests.TestEntity2" syncable="YES">
<attribute name="testBoolean" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
@@ -19,6 +21,8 @@
<attribute name="testNil" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="testNumber" optional="YES" attributeType="Integer 32" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testString" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="testToManyOrdered" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TestEntity2" inverseName="testToOne" inverseEntity="TestEntity2" syncable="YES"/>
<relationship name="testToOne" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TestEntity2" inverseName="testToManyOrdered" inverseEntity="TestEntity2" syncable="YES"/>
</entity>
<configuration name="Config1">
<memberEntity name="TestEntity1AAA"/>
@@ -27,7 +31,7 @@
<memberEntity name="TestEntity2"/>
</configuration>
<elements>
<element name="TestEntity1AAA" positionX="-63" positionY="-18" width="128" height="165"/>
<element name="TestEntity2" positionX="-63" positionY="9" width="128" height="165"/>
<element name="TestEntity1AAA" positionX="-63" positionY="-18" width="128" height="195"/>
<element name="TestEntity2" positionX="-63" positionY="9" width="128" height="195"/>
</elements>
</model>

View File

@@ -330,7 +330,14 @@ class SetupTests: BaseTestDataTestCase {
xcodeModelName: "Model",
bundle: Bundle(for: type(of: self))
)
try! stack.addStorageAndWait(sqliteStore)
try! stack.addStorageAndWait(
SQLiteStore.legacy(
fileName: sqliteStore.fileURL.lastPathComponent,
configuration: sqliteStore.configuration,
migrationMappingProviders: sqliteStore.migrationMappingProviders,
localStorageOptions: .recreateStoreOnModelMismatch
)
)
self.prepareTestDataForStack(stack)
}
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path))

View File

@@ -36,4 +36,6 @@ class TestEntity1: NSManagedObject {
@NSManaged var testDecimal: NSDecimalNumber?
@NSManaged var testData: Data?
@NSManaged var testNil: String?
@NSManaged var testToOne: TestEntity1?
@NSManaged var testToManyUnordered: NSSet?
}

View File

@@ -36,4 +36,6 @@ class TestEntity2: NSManagedObject {
@NSManaged var testDecimal: NSDecimalNumber?
@NSManaged var testData: Data?
@NSManaged var testNil: String?
@NSManaged var testToOne: TestEntity2?
@NSManaged var testToManyOrdered: NSOrderedSet?
}

View File

@@ -56,6 +56,163 @@ final class WhereTests: XCTestCase {
dynamic func test_ThatDynamicModelKeyPaths_CanBeCreated() {
XCTAssertEqual(String(keyPath: \TestEntity1.testEntityID), "testEntityID")
XCTAssertEqual(String(keyPath: \Animal.color), "color")
}
@objc
dynamic func test_ThatExpressions_HaveCorrectKeyPaths() {
do {
XCTAssertEqual(
#keyPath(TestEntity1.testToOne.testEntityID),
String(keyPath: \TestEntity1.testToOne ~ \.testEntityID)
)
XCTAssertEqual(
#keyPath(TestEntity1.testToOne.testToOne.testToManyUnordered),
String(keyPath: \TestEntity1.testToOne ~ \.testToOne ~ \.testToManyUnordered)
)
XCTAssertEqual(
#keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered),
String(keyPath: \TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered)
)
}
do {
XCTAssertEqual(
"master.pets",
String(keyPath: \Animal.master ~ \.pets)
)
XCTAssertEqual(
"master.pets.species",
String(keyPath: \Animal.master ~ \.pets ~ \.species)
)
XCTAssertEqual(
"master.pets.master",
String(keyPath: \Animal.master ~ \.pets ~ \.master)
)
}
do {
XCTAssertEqual(
#keyPath(TestEntity1.testToOne.testToManyUnordered) + ".@count",
(\TestEntity1.testToOne ~ \.testToManyUnordered).count().description
)
XCTAssertEqual(
#keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered) + ".@count",
(\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).count().description
)
}
do {
XCTAssertEqual(
"master.pets.@count",
(\Animal.master ~ \.pets).count().description
)
}
do {
XCTAssertEqual(
"ANY " + #keyPath(TestEntity1.testToOne.testToManyUnordered),
(\TestEntity1.testToOne ~ \.testToManyUnordered).any().description
)
XCTAssertEqual(
"ANY " + #keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered),
(\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).any().description
)
}
do {
XCTAssertEqual(
"ANY master.pets",
(\Animal.master ~ \.pets).any().description
)
XCTAssertEqual(
"ANY master.pets.species",
(\Animal.master ~ \.pets ~ \.species).any().description
)
}
do {
XCTAssertEqual(
"ALL " + #keyPath(TestEntity1.testToOne.testToManyUnordered),
(\TestEntity1.testToOne ~ \.testToManyUnordered).all().description
)
XCTAssertEqual(
"ALL " + #keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered),
(\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).all().description
)
}
do {
XCTAssertEqual(
"ALL master.pets",
(\Animal.master ~ \.pets).all().description
)
XCTAssertEqual(
"ALL master.pets.species",
(\Animal.master ~ \.pets ~ \.species).all().description
)
}
do {
XCTAssertEqual(
"NONE " + #keyPath(TestEntity1.testToOne.testToManyUnordered),
(\TestEntity1.testToOne ~ \.testToManyUnordered).none().description
)
XCTAssertEqual(
"NONE " + #keyPath(TestEntity2.testToOne.testToOne.testToManyOrdered),
(\TestEntity2.testToOne ~ \.testToOne ~ \.testToManyOrdered).none().description
)
}
do {
XCTAssertEqual(
"NONE master.pets",
(\Animal.master ~ \.pets).none().description
)
XCTAssertEqual(
"NONE master.pets.species",
(\Animal.master ~ \.pets ~ \.species).none().description
)
}
}
@objc
dynamic func test_ThatWhereClauses_CanBeCreatedFromExpressionsCorrectly() {
do {
let dummy = "dummy"
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testString) == dummy
let predicate = NSPredicate(format: "\(#keyPath(TestEntity1.testToOne.testString)) == %@", dummy)
XCTAssertEqual(whereClause, Where<TestEntity1>(predicate))
XCTAssertEqual(whereClause.predicate, predicate)
}
do {
let dummy = "dummy"
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testToOne ~ \.testString) == dummy
let predicate = NSPredicate(format: "\(#keyPath(TestEntity1.testToOne.testToOne.testString)) == %@", dummy)
XCTAssertEqual(whereClause, Where<TestEntity1>(predicate))
XCTAssertEqual(whereClause.predicate, predicate)
}
do {
let count = 3
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testToManyUnordered).count() == count
let predicate = NSPredicate(format: "\(#keyPath(TestEntity1.testToOne.testToManyUnordered)).@count == %d", count)
XCTAssertEqual(whereClause, Where<TestEntity1>(predicate))
XCTAssertEqual(whereClause.predicate, predicate)
}
do {
let dummy = "dummy"
let whereClause: Where<TestEntity1> = (\.testToOne ~ \.testToManyUnordered ~ \TestEntity1.testString).any() == dummy
let predicate = NSPredicate(format: "ANY \(#keyPath(TestEntity1.testToOne.testToManyUnordered)).\(#keyPath(TestEntity1.testString)) == %@", dummy)
XCTAssertEqual(whereClause, Where<TestEntity1>(predicate))
XCTAssertEqual(whereClause.predicate, predicate)
}
}
@objc

View File

@@ -1232,6 +1232,8 @@ extension NSAttributeType: CoreStoreDebugStringConvertible {
case .objectIDAttributeType: return ".objectIDAttributeType"
case .UUIDAttributeType: return ".UUIDAttributeType"
case .URIAttributeType: return ".URIAttributeType"
@unknown default:
fatalError()
}
}
}
@@ -1254,6 +1256,8 @@ extension NSDeleteRule: CoreStoreDebugStringConvertible {
case .nullifyDeleteRule: return ".nullifyDeleteRule"
case .cascadeDeleteRule: return ".cascadeDeleteRule"
case .denyDeleteRule: return ".denyDeleteRule"
@unknown default:
fatalError()
}
}
}

View File

@@ -66,8 +66,8 @@ extension CoreStore {
internal static func assert( _ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
self.logger.assert(
condition,
message: message,
condition(),
message: message(),
fileName: fileName,
lineNumber: lineNumber,
functionName: functionName

View File

@@ -47,7 +47,7 @@ public protocol DynamicKeyPath: AnyDynamicKeyPath {
/**
The DynamicObject type
*/
associatedtype ObjectType
associatedtype ObjectType: DynamicObject
/**
The Value type
@@ -66,7 +66,7 @@ extension KeyPathString {
let keyPath = String(keyPath: \Person.nickname)
```
*/
public init<O: NSManagedObject, V>(keyPath: KeyPath<O, V>) {
public init<O: NSManagedObject, V: AllowedObjectiveCKeyPathValue>(keyPath: KeyPath<O, V>) {
self = keyPath.cs_keyPathString
}
@@ -81,18 +81,24 @@ extension KeyPathString {
self = O.meta[keyPath: keyPath].cs_keyPathString
}
/**
Extracts the keyPath string from the property.
```
let keyPath = String(keyPath: \Person.nickname)
```
*/
public init<O: DynamicObject, T, V>(keyPath: Where<O>.Expression<T, V>) {
self = keyPath.cs_keyPathString
}
}
// MARK: - KeyPath: DynamicKeyPath
// TODO: SE-0143 (https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md) is implemented but multiple conformances for the same type currently cannot be declared.
//extension KeyPath: DynamicKeyPath where Root: NSManagedObject, Value: ImportableAttributeType
//extension KeyPath: DynamicKeyPath where Root: NSManagedObject, Value: ImportableAttributeType?
//extension KeyPath: DynamicKeyPath where Root: NSManagedObject, Value: NSManagedObject?
//extension KeyPath: DynamicKeyPath where Root: NSManagedObject, Value: NSSet
//extension KeyPath: DynamicKeyPath where Root: NSManagedObject, Value: NSOrderedSet
extension KeyPath: DynamicKeyPath {
extension KeyPath: DynamicKeyPath, AnyDynamicKeyPath where Root: DynamicObject, Value: AllowedObjectiveCKeyPathValue {
public typealias ObjectType = Root
public typealias ValueType = Value

View File

@@ -0,0 +1,140 @@
//
// KeyPathGenericBindings.swift
// CoreStore
//
// Copyright © 2018 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 CoreGraphics
import CoreData
// MARK: - AllowedObjectiveCKeyPathValue
/**
Used only for utility methods. Types allowed as `Value` generic type to `KeyPath` utilities.
*/
public protocol AllowedObjectiveCKeyPathValue {}
// MARK: - AllowedOptionalObjectiveCKeyPathValue
/**
Used only for utility methods. Types allowed as `Value` generic type to `KeyPath` utilities.
*/
public protocol AllowedOptionalObjectiveCKeyPathValue: AllowedObjectiveCKeyPathValue {}
extension Bool: AllowedObjectiveCKeyPathValue {}
extension CGFloat: AllowedObjectiveCKeyPathValue {}
extension Data: AllowedOptionalObjectiveCKeyPathValue {}
extension Date: AllowedOptionalObjectiveCKeyPathValue {}
extension Double: AllowedObjectiveCKeyPathValue {}
extension Float: AllowedObjectiveCKeyPathValue {}
extension Int: AllowedObjectiveCKeyPathValue {}
extension Int8: AllowedObjectiveCKeyPathValue {}
extension Int16: AllowedObjectiveCKeyPathValue {}
extension Int32: AllowedObjectiveCKeyPathValue {}
extension Int64: AllowedObjectiveCKeyPathValue {}
extension NSData: AllowedOptionalObjectiveCKeyPathValue {}
extension NSDate: AllowedOptionalObjectiveCKeyPathValue {}
extension NSManagedObject: AllowedOptionalObjectiveCKeyPathValue {}
extension NSNumber: AllowedOptionalObjectiveCKeyPathValue {}
extension NSString: AllowedOptionalObjectiveCKeyPathValue {}
extension NSSet: AllowedOptionalObjectiveCKeyPathValue {}
extension NSOrderedSet: AllowedOptionalObjectiveCKeyPathValue {}
extension NSURL: AllowedOptionalObjectiveCKeyPathValue {}
extension NSUUID: AllowedOptionalObjectiveCKeyPathValue {}
extension String: AllowedOptionalObjectiveCKeyPathValue {}
extension URL: AllowedOptionalObjectiveCKeyPathValue {}
extension UUID: AllowedOptionalObjectiveCKeyPathValue {}
extension Optional: AllowedObjectiveCKeyPathValue where Wrapped: AllowedOptionalObjectiveCKeyPathValue {}
// MARK: - AllowedObjectiveCCollectionKeyPathValue
/**
Used only for utility methods. Types allowed as `Value` generic type to `KeyPath` utilities.
*/
public protocol AllowedObjectiveCCollectionKeyPathValue: AllowedOptionalObjectiveCKeyPathValue {}
extension NSSet: AllowedObjectiveCCollectionKeyPathValue {}
extension NSOrderedSet: AllowedObjectiveCCollectionKeyPathValue {}
extension Optional: AllowedObjectiveCCollectionKeyPathValue, AllowedOptionalObjectiveCKeyPathValue where Wrapped: AllowedObjectiveCCollectionKeyPathValue {}
// MARK: - AllowedCoreStoreObjectKeyPathValue
/**
Used only for utility methods. Types allowed as `Value` generic type to `KeyPath` utilities.
*/
public protocol AllowedCoreStoreObjectKeyPathValue: DynamicKeyPath {}
extension ValueContainer.Required: AllowedCoreStoreObjectKeyPathValue {}
extension ValueContainer.Optional: AllowedCoreStoreObjectKeyPathValue {}
extension TransformableContainer.Required: AllowedCoreStoreObjectKeyPathValue {}
extension TransformableContainer.Optional: AllowedCoreStoreObjectKeyPathValue {}
extension RelationshipContainer.ToOne: AllowedCoreStoreObjectKeyPathValue {}
extension RelationshipContainer.ToManyOrdered: AllowedCoreStoreObjectKeyPathValue {}
extension RelationshipContainer.ToManyUnordered: AllowedCoreStoreObjectKeyPathValue {}
// MARK: - AllowedCoreStoreObjectCollectionKeyPathValue
/**
Used only for utility methods. Types allowed as `Value` generic type to `KeyPath` utilities.
*/
public protocol AllowedCoreStoreObjectCollectionKeyPathValue: AllowedCoreStoreObjectKeyPathValue {}
extension RelationshipContainer.ToManyOrdered: AllowedCoreStoreObjectCollectionKeyPathValue {}
extension RelationshipContainer.ToManyUnordered: AllowedCoreStoreObjectCollectionKeyPathValue {}

View File

@@ -1390,6 +1390,9 @@ extension ListMonitor: FetchedResultsControllerHandler {
"\(String(describing: IndexPath.self)).New": newIndexPath!
]
)
@unknown default:
fatalError()
}
}

View File

@@ -105,9 +105,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
keyPath: keyPath,
inverseKeyPath: { nil },
deleteRule: deleteRule,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -140,9 +140,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
keyPath: keyPath,
inverseKeyPath: { inverse(D.meta).keyPath },
deleteRule: deleteRule,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -175,9 +175,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
keyPath: keyPath,
inverseKeyPath: { inverse(D.meta).keyPath },
deleteRule: deleteRule,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -210,9 +210,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
keyPath: keyPath,
inverseKeyPath: { inverse(D.meta).keyPath },
deleteRule: deleteRule,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -354,9 +354,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
maxCount: maxCount,
inverseKeyPath: { nil },
deleteRule: deleteRule,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -395,9 +395,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
maxCount: maxCount,
inverseKeyPath: { inverse(D.meta).keyPath },
deleteRule: deleteRule,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -436,9 +436,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
maxCount: maxCount,
inverseKeyPath: { inverse(D.meta).keyPath },
deleteRule: deleteRule,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -477,9 +477,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
maxCount: maxCount,
inverseKeyPath: { inverse(D.meta).keyPath },
deleteRule: deleteRule,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -627,9 +627,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
deleteRule: deleteRule,
minCount: minCount,
maxCount: maxCount,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -668,9 +668,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
deleteRule: deleteRule,
minCount: minCount,
maxCount: maxCount,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -709,9 +709,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
deleteRule: deleteRule,
minCount: minCount,
maxCount: maxCount,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}
@@ -750,9 +750,9 @@ public enum RelationshipContainer<O: CoreStoreObject> {
deleteRule: deleteRule,
minCount: minCount,
maxCount: maxCount,
versionHashModifier: versionHashModifier,
renamingIdentifier: renamingIdentifier,
affectedByKeyPaths: affectedByKeyPaths
versionHashModifier: versionHashModifier(),
renamingIdentifier: renamingIdentifier(),
affectedByKeyPaths: affectedByKeyPaths()
)
}

View File

@@ -0,0 +1,304 @@
//
// Where.Expression.swift
// CoreStore
//
// Copyright © 2018 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: - ~
infix operator ~ : AdditionPrecedence
// MARK: - WhereExpressionTrait
public protocol WhereExpressionTrait {}
// MARK: - Where
extension Where {
// MARK: - Expression
public struct Expression<T: WhereExpressionTrait, V>: CustomStringConvertible, DynamicKeyPath {
public typealias Trait = T
// MARK: AnyDynamicKeyPath
public let cs_keyPathString: String
// MARK: DynamicKeyPath
public typealias ObjectType = D
public typealias ValueType = V
// MARK: CustomStringConvertible
public var description: String {
return self.cs_keyPathString
}
// MARK: Internal
internal init(_ component: String) {
self.cs_keyPathString = component
}
internal init(_ component1: String, _ component2: String) {
self.cs_keyPathString = component1 + "." + component2
}
}
// MARK: - SingleTarget
public enum SingleTarget: WhereExpressionTrait {}
// MARK: - CollectionTarget
public enum CollectionTarget: WhereExpressionTrait {}
}
// MARK: - ~ Operator (Where.Expression Creation Operators)
// MARK: ~ where D: NSManagedObject
public func ~<D: NSManagedObject, O: NSManagedObject, V: AllowedObjectiveCKeyPathValue>(_ lhs: KeyPath<D, O>, _ rhs: KeyPath<O, V>) -> Where<D>.Expression<Where<D>.SingleTarget, V> {
return .init(lhs.cs_keyPathString, rhs.cs_keyPathString)
}
public func ~<D: NSManagedObject, O: NSManagedObject, V: AllowedObjectiveCKeyPathValue>(_ lhs: KeyPath<D, O?>, _ rhs: KeyPath<O, V>) -> Where<D>.Expression<Where<D>.SingleTarget, V> {
return .init(lhs.cs_keyPathString, rhs.cs_keyPathString)
}
public func ~<D: NSManagedObject, O: NSManagedObject, V: AllowedObjectiveCCollectionKeyPathValue>(_ lhs: KeyPath<D, O>, _ rhs: KeyPath<O, V>) -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init(lhs.cs_keyPathString, rhs.cs_keyPathString)
}
public func ~<D: NSManagedObject, O: NSManagedObject, V: AllowedObjectiveCCollectionKeyPathValue>(_ lhs: KeyPath<D, O?>, _ rhs: KeyPath<O, V>) -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init(lhs.cs_keyPathString, rhs.cs_keyPathString)
}
public func ~<D: NSManagedObject, O: NSManagedObject, T, V: AllowedObjectiveCKeyPathValue>(_ lhs: Where<D>.Expression<T, O>, _ rhs: KeyPath<O, V>) -> Where<D>.Expression<T, V> {
return .init(lhs.cs_keyPathString, rhs.cs_keyPathString)
}
public func ~<D: NSManagedObject, O: NSManagedObject, T, V: AllowedObjectiveCCollectionKeyPathValue>(_ lhs: Where<D>.Expression<T, O>, _ rhs: KeyPath<O, V>) -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init(lhs.cs_keyPathString, rhs.cs_keyPathString)
}
public func ~<D: NSManagedObject, O: NSManagedObject, T, V: AllowedObjectiveCKeyPathValue>(_ lhs: Where<D>.Expression<T, O?>, _ rhs: KeyPath<O, V>) -> Where<D>.Expression<T, V> {
return .init(lhs.cs_keyPathString, rhs.cs_keyPathString)
}
public func ~<D: NSManagedObject, O: NSManagedObject, T, V: AllowedObjectiveCCollectionKeyPathValue>(_ lhs: Where<D>.Expression<T, O?>, _ rhs: KeyPath<O, V>) -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init(lhs.cs_keyPathString, rhs.cs_keyPathString)
}
public func ~<D: NSManagedObject, O: NSManagedObject, T, C: AllowedObjectiveCCollectionKeyPathValue, V: AllowedObjectiveCKeyPathValue>(_ lhs: Where<D>.Expression<T, C>, _ rhs: KeyPath<O, V>) -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init(lhs.cs_keyPathString, rhs.cs_keyPathString)
}
// MARK: - ~ where D: CoreStoreObject
public func ~<D: CoreStoreObject, O: CoreStoreObject, K: AllowedCoreStoreObjectKeyPathValue>(_ lhs: KeyPath<D, RelationshipContainer<D>.ToOne<O>>, _ rhs: KeyPath<O, K>) -> Where<D>.Expression<Where<D>.SingleTarget, K.ValueType> where K.ObjectType == O {
return .init(
D.meta[keyPath: lhs].cs_keyPathString,
O.meta[keyPath: rhs].cs_keyPathString
)
}
public func ~<D: CoreStoreObject, O: CoreStoreObject, K: AllowedCoreStoreObjectCollectionKeyPathValue>(_ lhs: KeyPath<D, RelationshipContainer<D>.ToOne<O>>, _ rhs: KeyPath<O, K>) -> Where<D>.Expression<Where<D>.CollectionTarget, K.ValueType> where K.ObjectType == O {
return .init(
D.meta[keyPath: lhs].cs_keyPathString,
O.meta[keyPath: rhs].cs_keyPathString
)
}
public func ~<D: CoreStoreObject, O: CoreStoreObject, T, K: AllowedCoreStoreObjectKeyPathValue>(_ lhs: Where<D>.Expression<T, O>, _ rhs: KeyPath<O, K>) -> Where<D>.Expression<T, K.ValueType> where K.ObjectType == O {
return .init(
lhs.cs_keyPathString,
O.meta[keyPath: rhs].cs_keyPathString
)
}
public func ~<D: CoreStoreObject, O: CoreStoreObject, T, K: AllowedCoreStoreObjectCollectionKeyPathValue>(_ lhs: Where<D>.Expression<T, O>, _ rhs: KeyPath<O, K>) -> Where<D>.Expression<Where<D>.CollectionTarget, K.ValueType> where K.ObjectType == O {
return .init(
lhs.cs_keyPathString,
O.meta[keyPath: rhs].cs_keyPathString
)
}
public func ~<D: CoreStoreObject, O: CoreStoreObject, T, K: AllowedCoreStoreObjectKeyPathValue>(_ lhs: Where<D>.Expression<T, O?>, _ rhs: KeyPath<O, K>) -> Where<D>.Expression<T, K.ValueType> where K.ObjectType == O {
return .init(
lhs.cs_keyPathString,
O.meta[keyPath: rhs].cs_keyPathString
)
}
public func ~<D: CoreStoreObject, O: CoreStoreObject, T, K: AllowedCoreStoreObjectCollectionKeyPathValue>(_ lhs: Where<D>.Expression<T, O?>, _ rhs: KeyPath<O, K>) -> Where<D>.Expression<Where<D>.CollectionTarget, K.ValueType> where K.ObjectType == O {
return .init(
lhs.cs_keyPathString,
O.meta[keyPath: rhs].cs_keyPathString
)
}
public func ~<D: CoreStoreObject, O: CoreStoreObject, T, KC: AllowedCoreStoreObjectCollectionKeyPathValue, KV: AllowedCoreStoreObjectKeyPathValue>(_ lhs: Where<D>.Expression<T, KC>, _ rhs: KeyPath<O, KV>) -> Where<D>.Expression<Where<D>.CollectionTarget, KV.ValueType> where KC.ObjectType == D, KV.ObjectType == O {
return .init(
lhs.cs_keyPathString,
O.meta[keyPath: rhs].cs_keyPathString
)
}
// MARK: - Where.Expression where V: QueryableAttributeType
public func == <D, T, V: QueryableAttributeType>(_ lhs: Where<D>.Expression<T, V>, _ rhs: V) -> Where<D> {
return Where<D>(lhs.cs_keyPathString, isEqualTo: rhs)
}
public func != <D, T, V: QueryableAttributeType>(_ lhs: Where<D>.Expression<T, V>, _ rhs: V) -> Where<D> {
return !Where<D>(lhs.cs_keyPathString, isEqualTo: rhs)
}
public func ~= <D, T, V: QueryableAttributeType, S: Sequence>(_ sequence: S, _ expression: Where<D>.Expression<T, V>) -> Where<D> where S.Iterator.Element == V {
return Where<D>(expression.cs_keyPathString, isMemberOf: sequence)
}
// MARK: - Where.Expression where V: Optional<QueryableAttributeType>
public func == <D, T, V: QueryableAttributeType>(_ lhs: Where<D>.Expression<T, V?>, _ rhs: V) -> Where<D> {
return Where<D>(lhs.cs_keyPathString, isEqualTo: rhs)
}
public func == <D, T, V: QueryableAttributeType>(_ lhs: Where<D>.Expression<T, V?>, _ rhs: V?) -> Where<D> {
return Where<D>(lhs.cs_keyPathString, isEqualTo: rhs)
}
public func != <D, T, V: QueryableAttributeType>(_ lhs: Where<D>.Expression<T, V?>, _ rhs: V) -> Where<D> {
return !Where<D>(lhs.cs_keyPathString, isEqualTo: rhs)
}
public func != <D, T, V: QueryableAttributeType>(_ lhs: Where<D>.Expression<T, V?>, _ rhs: V?) -> Where<D> {
return !Where<D>(lhs.cs_keyPathString, isEqualTo: rhs)
}
public func ~= <D, T, V: QueryableAttributeType, S: Sequence>(_ sequence: S, _ expression: Where<D>.Expression<T, V?>) -> Where<D> where S.Iterator.Element == V {
return Where<D>(expression.cs_keyPathString, isMemberOf: sequence)
}
// MARK: - Where.Expression where D: NSManagedObject, T == Where<D>.CollectionTarget, V: AllowedObjectiveCCollectionKeyPathValue
extension Where.Expression where D: NSManagedObject, T == Where<D>.CollectionTarget, V: AllowedObjectiveCCollectionKeyPathValue {
public func count() -> Where<D>.Expression<Where<D>.CollectionTarget, Int> {
return .init(self.cs_keyPathString, "@count")
}
}
// MARK: - Where.Expression where D: NSManagedObject, T == Where<D>.CollectionTarget, V: AllowedObjectiveCKeyPathValue
extension Where.Expression where D: NSManagedObject, T == Where<D>.CollectionTarget, V: AllowedObjectiveCKeyPathValue {
public func any() -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init("ANY " + self.cs_keyPathString)
}
public func all() -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init("ALL " + self.cs_keyPathString)
}
public func none() -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init("NONE " + self.cs_keyPathString)
}
}
// MARK: - Where.Expression where D: CoreStoreObject, T == Where<D>.CollectionTarget
extension Where.Expression where D: CoreStoreObject, T == Where<D>.CollectionTarget {
public func count() -> Where<D>.Expression<Where<D>.CollectionTarget, Int> {
return .init(self.cs_keyPathString, "@count")
}
public func any() -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init("ANY " + self.cs_keyPathString)
}
public func all() -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init("ALL " + self.cs_keyPathString)
}
public func none() -> Where<D>.Expression<Where<D>.CollectionTarget, V> {
return .init("NONE " + self.cs_keyPathString)
}
}

View File

@@ -39,41 +39,41 @@ public protocol WhereClauseType: AnyWhereClause {
associatedtype ObjectType: DynamicObject
}
extension WhereClauseType where Self.ObjectType: CoreStoreObject {
extension WhereClauseType {
/**
Combines two `Where` predicates together using `AND` operator.
- Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows AND'ing clauses of unrelated `DynamicObject` types.
*/
public static func && <TWhere: WhereClauseType>(left: Self, right: TWhere) -> Self {
return Self.init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate]))
public static func && <TWhere: WhereClauseType>(left: Self, right: TWhere) -> Where<Self.ObjectType> {
return .init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate]))
}
/**
Combines two `Where` predicates together using `AND` operator.
- Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows AND'ing clauses of unrelated `DynamicObject` types.
*/
public static func && <TWhere: WhereClauseType>(left: TWhere, right: Self) -> Self {
return Self.init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate]))
public static func && <TWhere: WhereClauseType>(left: TWhere, right: Self) -> Where<Self.ObjectType> {
return .init(NSCompoundPredicate(type: .and, subpredicates: [left.predicate, right.predicate]))
}
/**
Combines two `Where` predicates together using `OR` operator.
- Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows OR'ing clauses of unrelated `DynamicObject` types.
*/
public static func || <TWhere: WhereClauseType>(left: Self, right: TWhere) -> Self {
return Self.init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate]))
public static func || <TWhere: WhereClauseType>(left: Self, right: TWhere) -> Where<Self.ObjectType> {
return .init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate]))
}
/**
Combines two `Where` predicates together using `OR` operator.
- Warning: This operator overload is a workaround for Swift generics' inability to constrain by inheritance (https://bugs.swift.org/browse/SR-5213). In effect, this is less type-safe than other overloads because it allows OR'ing clauses of unrelated `DynamicObject` types.
*/
public static func || <TWhere: WhereClauseType>(left: TWhere, right: Self) -> Self {
return Self.init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate]))
public static func || <TWhere: WhereClauseType>(left: TWhere, right: Self) -> Where<Self.ObjectType> {
return .init(NSCompoundPredicate(type: .or, subpredicates: [left.predicate, right.predicate]))
}
}