#Predicate Pitfalls
Common runtime crashes and build errors when using #Predicate with SwiftData.
Unsupported Expressions
#Predicate compiles Swift expressions into a query representation. Only a subset of Swift is supported. Unsupported expressions compile but crash at runtime.
| Pattern | Crash? | Fix |
|---|---|---|
$0.name.uppercased() == "PARIS" |
Runtime crash | Use localizedStandardContains or caseInsensitiveCompare |
$0.name.count > 5 |
Runtime crash | Store length in a separate property or filter after fetch |
$0.tags.count == 0 |
Runtime crash | Use $0.tags.isEmpty (iOS 17.4+) or a stored flag |
$0.name.isEmpty on optional String |
Runtime crash on some OS versions | Use $0.name == nil || $0.name == "" |
| Custom computed property | Runtime crash | Only stored @Attribute properties work in predicates |
Date.now captured by value |
Stale predicate | Create a let now = Date() before the predicate and capture it |
| Enum raw value comparison | Runtime crash (pre-iOS 18) | Store the raw value as a separate property, or target iOS 18+ |
| Loops, declarations, mutation, or switch-heavy control flow | Build error or runtime failure | Use boolean logic, ternary expressions, optional chaining, or optional binding patterns supported by Foundation Predicate |
| Arbitrary method calls | Runtime crash | Only supported methods (see below) work |
Supported Operations
Comparisons: ==, !=, <, <=, >, >=
Logic: &&, ||, !
String: localizedStandardContains(_:), contains(_:), starts(with:), caseInsensitiveCompare(_:)
Collections: contains(where:), allSatisfy(_:), filter(_:), .isEmpty
Other: optional chaining, nil coalescing (??), ternary (? :), arithmetic (+, -, *, /), type casting (as?, is)
Safe Pattern: Build Predicates Dynamically
func tripPredicate(searchText: String, favoritesOnly: Bool) -> Predicate<Trip> {
let now = Date()
return #Predicate<Trip> { trip in
(searchText.isEmpty || trip.destination.localizedStandardContains(searchText))
&& (!favoritesOnly || trip.isFavorite)
&& trip.startDate > now
}
}Capture all external values as let bindings outside the predicate closure. The predicate captures them by value at creation time.
Debugging Predicate Crashes
When a predicate crashes at runtime with SwiftData.PredicateError or NSInvalidArgumentException:
- Simplify the predicate to a single clause and add clauses back one at a time
- Check each clause uses only supported operations on stored properties
- Test on the minimum deployment target — some operations were added in later iOS versions