Skip to content

Commit ceba8e1

Browse files
authored
Merge pull request #13 from ABridoux/develop
Added support for single boolean key path and string key paths
2 parents dc77545 + b94d76e commit ceba8e1

11 files changed

+229
-71
lines changed

Sources/SafeFetching/Predicate/Declarations/BooleanKeyPathPredicate+Comparison.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,7 @@ public func < <E: NSManagedObject, V: Comparable & DatabaseValue & DatabaseTestV
2828
public func <= <E: NSManagedObject, V: Comparable & DatabaseValue & DatabaseTestValue>(lhs: KeyPath<E, V>, rhs: V) -> Builders.Predicate<E> {
2929
.init(keyPath: lhs, operatorString: "<=", value: rhs)
3030
}
31+
32+
public prefix func ! <E: NSManagedObject>(rhs: KeyPath<E, Bool>) -> Builders.Predicate<E> {
33+
.init(keyPath: rhs, isInverted: true)
34+
}

Sources/SafeFetching/Predicate/Declarations/BooleanStringKeyPathPredicate+Comparison.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,9 @@ public func <= <E: NSManagedObject, V: Comparable & DatabaseTestValue>(
4848
) -> Builders.Predicate<E> {
4949
.init(keyPathString: lhs.key, operatorString: "<=", value: rhs)
5050
}
51+
52+
public prefix func ! <E: NSManagedObject>(
53+
rhs: StringKeyPath<E, Bool>
54+
) -> Builders.Predicate<E> {
55+
.init(keyPathString: rhs.key, isInverted: true)
56+
}

Sources/SafeFetching/Predicate/Declarations/StringKeyPredicateRightValue+RawRepresentable.swift

Lines changed: 0 additions & 64 deletions
This file was deleted.

Sources/SafeFetching/Predicate/Types/CompoundPredicate.swift

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ extension Builders {
3131
}
3232
}
3333

34+
// MARK: - Predicate <-> Predicate
35+
3436
public func && <E: NSManagedObject>(
3537
lhs: Builders.Predicate<E>,
3638
rhs: Builders.Predicate<E>
@@ -52,3 +54,99 @@ public func || <E: NSManagedObject>(
5254
rightPredicate: rhs
5355
)
5456
}
57+
58+
// MARK: - KeyPath <-> Predicate
59+
60+
public func && <E: NSManagedObject>(
61+
lhs: KeyPath<E, Bool>,
62+
rhs: Builders.Predicate<E>
63+
) -> Builders.CompoundPredicate<E> {
64+
Builders.CompoundPredicate(
65+
joinOperator: .and,
66+
leftPredicate: .init(keyPath: lhs),
67+
rightPredicate: rhs
68+
)
69+
}
70+
71+
public func || <E: NSManagedObject>(
72+
lhs: KeyPath<E, Bool>,
73+
rhs: Builders.Predicate<E>
74+
) -> Builders.CompoundPredicate<E> {
75+
Builders.CompoundPredicate(
76+
joinOperator: .or,
77+
leftPredicate: .init(keyPath: lhs),
78+
rightPredicate: rhs
79+
)
80+
}
81+
82+
// MARK: - Predicate <-> KeyPath
83+
84+
public func && <E: NSManagedObject>(
85+
lhs: Builders.Predicate<E>,
86+
rhs: KeyPath<E, Bool>
87+
) -> Builders.CompoundPredicate<E> {
88+
Builders.CompoundPredicate(
89+
joinOperator: .and,
90+
leftPredicate: lhs,
91+
rightPredicate: .init(keyPath: rhs)
92+
)
93+
}
94+
95+
public func || <E: NSManagedObject>(
96+
lhs: Builders.Predicate<E>,
97+
rhs: KeyPath<E, Bool>
98+
) -> Builders.CompoundPredicate<E> {
99+
Builders.CompoundPredicate(
100+
joinOperator: .or,
101+
leftPredicate: lhs,
102+
rightPredicate: .init(keyPath: rhs)
103+
)
104+
}
105+
106+
// MARK: - StringKeyPath <-> Predicate
107+
108+
public func && <E: NSManagedObject>(
109+
lhs: StringKeyPath<E, Bool>,
110+
rhs: Builders.Predicate<E>
111+
) -> Builders.CompoundPredicate<E> {
112+
Builders.CompoundPredicate(
113+
joinOperator: .and,
114+
leftPredicate: .init(keyPathString: lhs.key),
115+
rightPredicate: rhs
116+
)
117+
}
118+
119+
public func || <E: NSManagedObject>(
120+
lhs: StringKeyPath<E, Bool>,
121+
rhs: Builders.Predicate<E>
122+
) -> Builders.CompoundPredicate<E> {
123+
Builders.CompoundPredicate(
124+
joinOperator: .or,
125+
leftPredicate: .init(keyPathString: lhs.key),
126+
rightPredicate: rhs
127+
)
128+
}
129+
130+
// MARK: - Predicate <-> StringKeyPath
131+
132+
public func && <E: NSManagedObject>(
133+
lhs: Builders.Predicate<E>,
134+
rhs: StringKeyPath<E, Bool>
135+
) -> Builders.CompoundPredicate<E> {
136+
Builders.CompoundPredicate(
137+
joinOperator: .and,
138+
leftPredicate: lhs,
139+
rightPredicate: .init(keyPathString: rhs.key)
140+
)
141+
}
142+
143+
public func || <E: NSManagedObject>(
144+
lhs: Builders.Predicate<E>,
145+
rhs: StringKeyPath<E, Bool>
146+
) -> Builders.CompoundPredicate<E> {
147+
Builders.CompoundPredicate(
148+
joinOperator: .or,
149+
leftPredicate: lhs,
150+
rightPredicate: .init(keyPathString: rhs.key)
151+
)
152+
}

Sources/SafeFetching/Predicate/Types/Predicate.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,18 @@ extension Builders.Predicate {
3838
isInverted: Bool = false,
3939
formatter: @escaping Formatter
4040
) {
41-
4241
let format = formatter(keyPath.label)
4342
self.init(nsValue: NSPredicate(format: format))
4443
}
44+
45+
public convenience init(
46+
keyPath: KeyPath<Entity, Bool>,
47+
isInverted: Bool = false
48+
) {
49+
let testValue = isInverted ? "0" : "1"
50+
let format = "\(keyPath.label) == \(testValue)"
51+
self.init(nsValue: NSPredicate(format: format))
52+
}
4553
}
4654

4755
// MARK: - KeyPath string init
@@ -67,4 +75,13 @@ extension Builders.Predicate {
6775
let format = formatter(keyPathString)
6876
self.init(nsValue: NSPredicate(format: format))
6977
}
78+
79+
public convenience init(
80+
keyPathString: String,
81+
isInverted: Bool = false
82+
) {
83+
let testValue = isInverted ? "0" : "1"
84+
let format = "\(keyPathString) == \(testValue)"
85+
self.init(nsValue: NSPredicate(format: format))
86+
}
7087
}

Sources/SafeFetching/Request/RequestBuilder+BuildingSteps.swift

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,30 @@ public extension Builders.Request where Step == TargetStep {
5959
/// ### Examples
6060
/// - `.where(\.name == "Endo")`
6161
/// - `.where(\.age >= 20 && \.score * .isIn(10...20))`
62-
func `where`(_ predicate: Builders.Predicate<Entity>)
63-
-> Builders.Request<Entity, PredicateStep, Output> {
62+
func `where`(_ predicate: Builders.Predicate<Entity>) -> Builders.Request<Entity, PredicateStep, Output> {
6463
request.predicate = predicate.nsValue
6564
return .init(request: request)
6665
}
66+
67+
/// Pass a boolean key path
68+
///
69+
/// ### Examples
70+
/// - `.where(\.isDownloaded)`
71+
/// - `.where(!\.isDownloaded)`
72+
func `where`(_ keyPath: KeyPath<Entity, Bool>) -> Builders.Request<Entity, PredicateStep, Output> {
73+
request.predicate = Builders.Predicate<Entity>(keyPath: keyPath).nsValue
74+
return .init(request: request)
75+
}
76+
77+
/// Pass a boolean string key path
78+
///
79+
/// ### Examples
80+
/// - `.where(.isDownloaded)`
81+
/// - `.where(!.isDownloaded)`
82+
func `where`(_ keyPath: StringKeyPath<Entity, Bool>) -> Builders.Request<Entity, PredicateStep, Output> {
83+
request.predicate = Builders.Predicate<Entity>(keyPathString: keyPath.key).nsValue
84+
return .init(request: request)
85+
}
6786
}
6887

6988

Sources/SafeFetching/SafeFetching.docc/Articles/build-predicates.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ final class StubEntity: NSManagedObject {
1212
}
1313
```
1414

15-
When building a request, the ``Builders/Request/where(_:)`` operation allows to specify a predicate. For a demonstration purpose in this article, predicates are specified after their implicit declaration.
15+
When building a request, the ``Builders/Request/where(_:)-6r3wg`` operation allows to specify a predicate. For a demonstration purpose in this article, predicates are specified after their implicit declaration.
1616

1717
```swift
1818
let predicate: Builders.Predicate<StubEntity>
@@ -53,6 +53,22 @@ predicate = \.score < 20
5353
predicate = \.score <= 20
5454
```
5555

56+
##### Boolean
57+
58+
```swift
59+
predicate = \.isAdmin == true
60+
```
61+
62+
```swift
63+
predicate = !\.isAdmin
64+
```
65+
66+
The `where(_:)` function has convenient variations to take a single boolean like ``Builders/Request/where(_:)-5ar9o``.
67+
68+
```swift
69+
.where(\.isAdmin)
70+
```
71+
5672
## Advanced operations
5773
It's possible to use the advanced operators offered by `NSPredicate` safely by specifying a key path followed by the junction operator `*` then the custom operator.
5874

@@ -101,10 +117,22 @@ predicate = \.name == "Bruce"
101117
|| \.score * .isIn(20..<40)
102118
```
103119

104-
Composing predicates with compound predicates is done naturally
120+
##### Single booleans
121+
122+
Compound predicates work with single booleans.
123+
124+
```swift
125+
predicate = \.isAdmin && \.score * .isIn(20..<40)
126+
```
127+
128+
```swift
129+
predicate = !\.isAdmin || \.score * .isIn(20..<40)
130+
```
105131

106132
### And - And
107133

134+
Composing predicates with compound predicates is done naturally
135+
108136
```swift
109137
predicate = \.score * .isIn(20..<40)
110138
&& \.name * .hasPrefix("Do")

Sources/SafeFetching/SafeFetching.docc/SafeFetching.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Key paths predicate work with key paths that target a property exposed to Object
2727
- ``_=(_:_:)-3bumn``
2828
- ``_(_:_:)-2o0zw``
2929
- ``_=(_:_:)-7tt3x``
30+
- ``!(_:)-k9g0``
3031

3132
### Key path advanced predicates
3233

@@ -46,6 +47,7 @@ Advanced predicates like string comparison ``Builders/KeyPathPredicateRightValue
4647
- ``_=(_:_:)-7schv``
4748
- ``_(_:_:)-756zh``
4849
- ``_=(_:_:)-627jw``
50+
- ``!(_:)-84xed``
4951

5052

5153
### String key paths advanced predicates
@@ -56,9 +58,19 @@ Advanced predicates like membership ``Builders/StringKeyPathPredicateRightValue/
5658

5759
### Compound predicate
5860

61+
Use and `&&` and or `||` operators between predicates or with a single boolean key path or string key path..
62+
5963
- ``Builders/CompoundPredicate``
60-
- ``&&(_:_:)``
61-
- ``__(_:_:)``
64+
- ``&&(_:_:)-9k2h3``
65+
- ``__(_:_:)-7vb5w``
66+
- ``&&(_:_:)-5awsl``
67+
- ``__(_:_:)-2bklc``
68+
- ``&&(_:_:)-1r2e1``
69+
- ``__(_:_:)-91dq6``
70+
- ``&&(_:_:)-26dgm``
71+
- ``__(_:_:)-k20``
72+
- ``&&(_:_:)-1vytf``
73+
- ``__(_:_:)-6zf3v``
6274

6375
### Build requests
6476

Tests/SafeFetchingTests/BooleanPredicateBuilderTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ final class BooleanPredicateTests: XCTestCase {
1717
testNSFormat(predicate: \.score <= 10, expecting: "score <= 10")
1818
testNSFormat(predicate: \.isAdmin == true, expecting: "isAdmin == 1")
1919
testNSFormat(predicate: \.isAdmin == false, expecting: "isAdmin == 0")
20+
testNSFormat(predicate: !\.isAdmin, expecting: "isAdmin == 0")
2021
testNSFormat(predicate: \.property == nil, expecting: "property == nil")
22+
2123
testNSFormat(predicate: \.stubRelationship == nil, expecting: "stubRelationship == nil")
2224
}
2325

0 commit comments

Comments
 (0)