Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Win a iOSKonf '24 conference ticket with thisfree giveaway

Predicate Macro in Swift for filtering and searching

#Predicate is a new Macro available since Swift 5.9 and Xcode 15, allowing you to filter or search a data collection. It can be seen as a replacement for the old-fashioned NSPredicate we’re used to from the Objective-C days.

The new Predicate is available as a Macro. If you’re new to Macros, I encourage you to read Swift Macros: Extend Swift with New Kinds of Expressions first.

What is a predicate?

A predicate allows you to evaluate an instance by applying a logical condition resulting in a Boolean value, true or false. You typically use them for filtering a collection or searching specific elements.

For example, imagine having a collection of articles:

struct Article {
    let title: String
    let author: String
}

let articles = [
    Article(title: "Swift Macros", author: "Antoine van der Lee"),
    Article(title: "What's new in Swift 5.9", author: "Paul Hudson"),
    Article(title: "Xcode 15: Automated accessibility audits", author: "Pol Piela")
]

In case you want to filter on SwiftLee articles only, you could write the following predicate:

let swiftLeeArticlesPredicate = #Predicate<Article> { article in
    article.title.contains("Swift") && article.author == "Antoine van der Lee"
}

You can use the outcome predicate as an argument of the filter method:

let swiftLeeArticles = try articles
    .filter(swiftLeeArticlesPredicate)
    .map(\.title)
print(swiftLeeArticles) // ["Swift Macros"]

The outcome contains titles for all SwiftLee articles found in the articles array.

How to use the #Predicate Macro

We’ve already seen an example of using the #Predicate Macro in Swift. It’s usage is relatively straightforward using a closure containing an instance of the generic type.

In our case, the generic type conforms to Article, meaning we can apply logical conditions based on article parameters. You could use single or multiple conditions, depending on your needs. Here are a few more examples:

#Predicate<Article> { article in
    /// A single condition for a specific title
    article.title == "Swift Macros"
}

/// Returning both articles written by Pol or Paul
_ = #Predicate<Article> { article in
    article.author == "Paul Hudson" || article.author == "Pol Piela"
}

/// Returning all articles that don't have Swift in their title.
_ = #Predicate<Article> { article in
    !article.title.contains("swift")
}

A look at the underlying implementation

Swift Macros are still relatively new and might create the illusion of magic behind the scenes. Many of us will only use Macros without caring about the underlying implementation. However, I recommend peeking behind the scenes to find out what code is getting generated by the Macro:

The Predicate macro, as expanded inside Xcode, shows the underlying implementation.
The Predicate macro, as expanded inside Xcode, shows the underlying implementation.

A Macro is a compiler plugin that generates code during the build phase. The output code might look intimidating, but it’s good to realize that the Macro is doing this for you. Theoretically, you could write this code manually without using the #Predicate, but it’s much harder to do.

If we look closer to the Predicate definition inside the Foundation framework, we can conclude it’s available on iOS 17 and up:

@available(macOS 14, iOS 17, tvOS 17, watchOS 10, *)
public struct Predicate<each Input> : Sendable

Furthermore, it conforms to Sendable and uses Swift’s new feature, Parameter Packs.

It’s great to realize we don’t have to care about this underlying implementation. The Macro allows you to write predicates with easier-to-understand, higher-level code.

Conclusion

Swift’s replacement for NSPredicate using the #Predicate macro allows you to write filtering or search logic using type-safe, readable code. The underlying implementation uses a sendable predicate struct that builds out the predicate expressions. Luckily, you don’t have to care about the underlying implementation, and you can benefit from easier-to-write, higher-level code.

If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.

Thanks!

 

Stay updated with the latest in Swift

The 2nd largest newsletter in the Apple development community with 18,327 developers.


Featured SwiftLee Jobs

Find your next Swift career step at world-class companies with impressive apps by joining the SwiftLee Talent Collective. I'll match engineers in my collective with exciting app development companies. SwiftLee Jobs