Skip to content

Add support for Foundation.Predicate - #34

Merged
colemancda merged 2 commits into
masterfrom
feature/predicate
Aug 16, 2026
Merged

colemancda merged 2 commits into
masterfrom
feature/predicate

Conversation

@colemancda

@colemancda colemancda commented Aug 16, 2026

Copy link
Copy Markdown
Member

Resolves #15

Converts predicates built with the #Predicate macro into FetchRequest.Predicate by walking the PredicateExpressions tree, cross-platform via swift-foundation's FoundationEssentials — and extends the CoreModel predicate model and in-memory evaluator so everything CoreData can represent converts and evaluates consistently.

Foundation.Predicate conversion

  • New throwing initializer FetchRequest.Predicate.init(_: Foundation.Predicate<T>) (macOS 14 / iOS 17 / tvOS 17 / watchOS 10, matching Foundation.Predicate availability).
  • Comparisons (==, !=, <, <=, >, >=), compounds (&& / || flattened into single compounds, !), contains, starts(with:), localizedStandardContains (Darwin, mapped to CONTAINS[cd]), boolean properties used directly as predicates, constants, and nil comparisons.
  • Arithmetic: +, -, *, /, %, and unary minus convert to the new ArithmeticExpression.
  • Collection closures: allSatisfy { ... } and contains(where:) { ... } convert to ALL/ANY modifier comparisons via variable-binding contexts (single-comparison tests; compound tests throw).
  • Aggregates: min() / max() convert to @min / @max key path operators.
  • Ranges: 18...30 lowers to >= lower && <= upper, half-open 18..<30 to >= lower && < upper (BETWEEN isn't evaluatable).
  • Key paths resolve to property names via Key-Value Coding on Darwin, falling back to parsing the key path's reflection-based debugDescription, so plain Swift structs work on all platforms.
  • Regex: contains(regex) converts to a MATCHES comparison. The macro wraps regexes in PredicateExpressions.PredicateRegex, which retains the source pattern, so literals, Regex(String), and RegexBuilder regexes all convert; the pattern is padded with .* because MATCHES matches the whole value (requires macOS 15 / iOS 18, where those types are available).
  • Unsupported expressions (subscripts, type casts, ??, nested closures) throw a descriptive ConversionError instead of silently misconverting.

CoreModel predicate model

  • New FetchRequest.Predicate.ArithmeticExpression (add/subtract/multiply/divide/modulus) as a new Expression case, with Codable support and in-memory evaluation: integer operands stay integral, division is always floating-point (matching NSExpression's divide:by:), division by zero and floating-point remainder resolve to no match.
  • Relationship key path traversal in the in-memory evaluator: events.name now resolves through to-one and to-many relationships against an object index, so ALL/ANY predicates produce the same results in memory as they do in CoreData. Previously these silently matched nothing. FetchRequest.evaluate(_:) builds the index from the objects it is given, and InMemoryStorage now supplies every entity's objects so relationships resolve.

CoreDataModel

  • Arithmetic expressions bridge to NSExpression(forFunction:) — the Function raw values are the NSExpression function names.
  • Function detection for the in-memory fallback path recurses into arithmetic operands.

Tests

  • ArithmeticExpressionTests: CoreModel API construction, description, Codable round-trip, evaluation semantics (promotion, division by zero, non-numeric operands), fetch request evaluation.
  • FoundationPredicateTests: every supported #Predicate expression shape, converted-tree assertions, in-memory filtering, and error paths.
  • KeyPathTraversalTests: to-one and to-many traversal, ALL/ANY, unresolved relationships, InMemoryStorage fetches, and the same traversals built with #Predicate.
  • CoreDataModelTests: end-to-end fetches against a CoreData store using arithmetic and ALL/ANY predicates built both from #Predicate and the CoreModel API, including a check that empty to-many ALL is vacuously true in CoreData and that the in-memory evaluator agrees on the same objects.

@github-code-quality

Copy link
Copy Markdown

Code Coverage Overview

Languages: Swift

Swift / code-coverage/llvm-cov

The overall coverage in commit 818c137 in the feature/predicate branch remains at 95%, unchanged from commit fff8b7b in the master branch.

Show a code coverage summary of the most impacted files.
File master fff8b7b feature/predicate 818c137 +/-
Sources/CoreMod...Predicate.swift 0% 85% +85%

@colemancda
colemancda merged commit ea30b8b into master Aug 16, 2026
32 checks passed
@colemancda
colemancda deleted the feature/predicate branch August 16, 2026 14:26
@colemancda
colemancda restored the feature/predicate branch August 16, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for Foundation.Predicate

1 participant