From 3bb2639e251c893f49ebf4f88ec4201c77343399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fai=C3=A7al=20Tchirou?= Date: Fri, 1 Mar 2024 20:57:13 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20a=20crash=20caused=20by=20?= =?UTF-8?q?UUID=20properties=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PredicateKit.podspec | 2 +- PredicateKit/Predicate.swift | 28 ++++++++++++++++++- PredicateKit/Primitive.swift | 7 +++++ .../NSFetchRequestBuilderTests.swift | 22 +++++++++++++++ PredicateKitTests/PrimitiveTests.swift | 21 ++++++++++++++ 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/PredicateKit.podspec b/PredicateKit.podspec index 978941f..385dd21 100644 --- a/PredicateKit.podspec +++ b/PredicateKit.podspec @@ -15,7 +15,7 @@ Pod::Spec.new do |spec| spec.name = "PredicateKit" - spec.version = "1.7.0" + spec.version = "1.8.0" spec.summary = "Write expressive and type-safe predicates for CoreData using key-paths, comparisons and logical operators, literal values, and functions." spec.description = <<-DESC PredicateKit allows Swift developers to write expressive and type-safe predicates for CoreData using key-paths, comparisons and logical operators, literal values, and functions. diff --git a/PredicateKit/Predicate.swift b/PredicateKit/Predicate.swift index 6b061c1..9109d55 100644 --- a/PredicateKit/Predicate.swift +++ b/PredicateKit/Predicate.swift @@ -349,6 +349,7 @@ public struct ComparisonOptions: OptionSet { public static let caseInsensitive = ComparisonOptions(rawValue: 1 << 0) public static let diacriticInsensitive = ComparisonOptions(rawValue: 1 << 1) public static let normalized = ComparisonOptions(rawValue: 1 << 2) + public static let none = ComparisonOptions(rawValue: 1 << 3) public init(rawValue: Int) { self.rawValue = rawValue @@ -718,11 +719,22 @@ extension Optional: ComparableCollection where Wrapped: ComparableCollection { // MARK: - Private Initializers extension Comparison { + fileprivate init( + _ expression: E, + _ `operator`: ComparisonOperator, + _ value: P + ) { + self.expression = AnyExpression(expression) + self.operator = `operator` + self.value = value + self.options = P.defaultComparisonOptions + } + fileprivate init( _ expression: E, _ `operator`: ComparisonOperator, _ value: Primitive, - _ options: ComparisonOptions = .caseInsensitive + _ options: ComparisonOptions ) { self.expression = AnyExpression(expression) self.operator = `operator` @@ -759,3 +771,17 @@ extension ArrayElementKeyPath { } } } + +extension Primitive { + static var defaultComparisonOptions: ComparisonOptions { + switch type { + case .uuid: + return .none + + // TODO: Add proper defaults for the other types. + // For now, .caseInsensitive does not seem to hurt? + default: + return .caseInsensitive + } + } +} diff --git a/PredicateKit/Primitive.swift b/PredicateKit/Primitive.swift index 41d4f34..08ac690 100644 --- a/PredicateKit/Primitive.swift +++ b/PredicateKit/Primitive.swift @@ -160,3 +160,10 @@ extension Optional: Comparable where Wrapped: Comparable { } } } + +@available(iOS 13.0, watchOS 6.0, tvOS 13.0, *) +extension Optional: Identifiable where Wrapped: Identifiable { + public var id: Wrapped.ID? { + self?.id + } +} diff --git a/PredicateKitTests/CoreDataTests/NSFetchRequestBuilderTests.swift b/PredicateKitTests/CoreDataTests/NSFetchRequestBuilderTests.swift index 9c13b7d..1bb2438 100644 --- a/PredicateKitTests/CoreDataTests/NSFetchRequestBuilderTests.swift +++ b/PredicateKitTests/CoreDataTests/NSFetchRequestBuilderTests.swift @@ -109,6 +109,27 @@ final class NSFetchRequestBuilderTests: XCTestCase { XCTAssertEqual(comparison.comparisonPredicateModifier, .direct) } + @available(iOS 13.0, watchOS 6.0, tvOS 13.0, *) + func testEqualityWithOptionalIdentifiable() throws { + guard let identifiable = makeIdentifiable() else { + XCTFail("could not initialize IdentifiableData") + return + } + + identifiable.id = "42" + + let request = makeRequest(\Data.optionalIdentifiable == identifiable) + let builder = makeRequestBuilder() + + let result: NSFetchRequest = builder.makeRequest(from: request) + + let comparison = try XCTUnwrap(result.predicate as? NSComparisonPredicate) + XCTAssertEqual(comparison.leftExpression, NSExpression(forKeyPath: "optionalIdentifiable.id")) + XCTAssertEqual(comparison.rightExpression, NSExpression(forConstantValue: "42")) + XCTAssertEqual(comparison.predicateOperatorType, .equalTo) + XCTAssertEqual(comparison.comparisonPredicateModifier, .direct) + } + func testArrayElementEqualPredicate() throws { let request = makeRequest((\Data.relationships).last(\.count) == 42) let builder = makeRequestBuilder() @@ -1134,6 +1155,7 @@ private class Data: NSManagedObject { @NSManaged var optionalRelationship: Relationship? @NSManaged var optionalRelationships: [Relationship]? @NSManaged var identifiable: IdentifiableData + @NSManaged var optionalIdentifiable: IdentifiableData? } private class Relationship: NSManagedObject { diff --git a/PredicateKitTests/PrimitiveTests.swift b/PredicateKitTests/PrimitiveTests.swift index a67f933..7c35376 100644 --- a/PredicateKitTests/PrimitiveTests.swift +++ b/PredicateKitTests/PrimitiveTests.swift @@ -158,4 +158,25 @@ final class PrimitiveTests: XCTestCase { XCTAssertFalse(nil < rhs) XCTAssertFalse(lhs < nil) } + + func testDefaultComparisonOptions() { + XCTAssertEqual(UUID.defaultComparisonOptions, .none) + XCTAssertEqual(Bool.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(Int.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(Int8.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(Int16.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(Int32.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(Int64.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(UInt.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(UInt8.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(UInt16.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(UInt32.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(UInt64.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(Double.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(Float.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(String.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(Date.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(URL.defaultComparisonOptions, .caseInsensitive) + XCTAssertEqual(Data.defaultComparisonOptions, .caseInsensitive) + } }