Skip to content

Commit

Permalink
🐛 Fix a crash caused by UUID properties (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
ftchirou authored Mar 1, 2024
1 parent 1dbb2c5 commit 3bb2639
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 2 deletions.
2 changes: 1 addition & 1 deletion PredicateKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
28 changes: 27 additions & 1 deletion PredicateKit/Predicate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -718,11 +719,22 @@ extension Optional: ComparableCollection where Wrapped: ComparableCollection {
// MARK: - Private Initializers

extension Comparison {
fileprivate init<E: Expression, P: Primitive>(
_ expression: E,
_ `operator`: ComparisonOperator,
_ value: P
) {
self.expression = AnyExpression(expression)
self.operator = `operator`
self.value = value
self.options = P.defaultComparisonOptions
}

fileprivate init<E: Expression>(
_ expression: E,
_ `operator`: ComparisonOperator,
_ value: Primitive,
_ options: ComparisonOptions = .caseInsensitive
_ options: ComparisonOptions
) {
self.expression = AnyExpression(expression)
self.operator = `operator`
Expand Down Expand Up @@ -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
}
}
}
7 changes: 7 additions & 0 deletions PredicateKit/Primitive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
22 changes: 22 additions & 0 deletions PredicateKitTests/CoreDataTests/NSFetchRequestBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Data> = 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()
Expand Down Expand Up @@ -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 {
Expand Down
21 changes: 21 additions & 0 deletions PredicateKitTests/PrimitiveTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

0 comments on commit 3bb2639

Please sign in to comment.