CompoundPredicate: A small library to combine Predicates

Hello Swift Community,

Lately, I've been increasingly frustrated with the limitations of Predicates in Swift, especially when it comes to combining them, unlike NSPredicate. The documentation on this topic has been rather vague, leading to numerous failed attempts and a descent into madness, which I've documented in my Stack Overflow post and in Fatbobman's article here.

However, I come bearing good news! I'm thrilled to announce the release of "CompoundPredicate"

This small library addresses the issue of combining predicates without the need for a custom PredicateExpression (which is not supported in SwiftData) and without the hassle of manually constructing expressions.

You can find the source code and documentation on GitHub.

Using it to combine predicates is easy:

import CompoundPredicate


let notTooShort = #Predicate<Book> {
    $0.pages > 50
}

let notTooLong = #Predicate<Book> {
    $0.pages <= 350
}

let lengthFilter = [notTooShort, notTooShort].conjunction()

// Match Books that are just the right length
let titleFilter = #Predicate<Book> {
    $0.title.contains("Swift")
}

// Match Books that contain "Swift" in the title or
// are just the right length
let filter = [lengthFilter, titleFilter].disjunction()
2 Likes

Hey @NoahKamara, thanks for sharing! This is super neat and I'm happy to see some new thoughts around potential APIs for Predicate!

In addition to your library, I wanted to share that Foundation now has support for this as well as of macOS 14.4 / iOS 17.4. Your example above can be written with the new Foundation support via the following:

let notTooShort = #Predicate<Book> { $0.pages > 50 }
let notTooLong = #Predicate<Book> { $0.pages <= 350 }
let titleFilter = #Predicate<Book> { $0.title.contains("Swift") }

let filter = #Predicate<Book> {
    (notTooShort.evaluate($0) && notTooLong.evaluate($0)) || titleFilter.evaluate($0)
}

This capability might not be quite as easy to discover as a function like conjunction/disjunction on Predicate itself like you have in your library, but should support compound predicates as well as more complex constructions beyond just conjunctions/disjunctions. Hope this helps a bit in your Predicate endeavors! You can see more details about the Foundation change in this PR on the swift-foundation repo.

2 Likes

Thanks for the addition @jmschonfeld I'll mention the PR in the README for devs targeting 14.4 :+1: