How can I make a dynamic SwiftData predicate?

Generated on 7/30/2024

1 search

Tailscan

This post is brought to you by Tailscan, the best devtool for Tailwind CSS.

To make a dynamic SwiftData predicate, you can use the new expression macro introduced in iOS 18. This allows you to build complex predicates easily by using expressions that can reference values and perform complex evaluations. Here’s a step-by-step guide based on the context provided:

  1. Basic Predicate: Start by building a basic predicate using user-provided search text. For example, if you have a search bar in your app, you can filter trips by checking if the trip's name contains the search text.

  2. Compound Predicate: If the search text might apply to more than just a trip's name, you can build a compound predicate to also check other properties, such as the destination property on a trip.

  3. Using Expressions: To create more complex predicates, you can use the new expression macro. Expressions allow for reference values that do not produce true or false but instead allow for arbitrary types. These can be used to represent complex evaluations using a model's properties and can be composed within predicates.

  4. Example: In the Trips app, you might want to create a query that fetches any ongoing trip where there are still some sites left to see. This can be done by building a predicate that specifies the trip should be ongoing (current date falls within the start and end dates) and that at least one of the bucket list items on the trip has the isinplan property set to false. You can construct an expression to count the number of bucket list items that haven't been planned and use this expression in your predicate.

Here’s a code snippet to illustrate this:

import SwiftData

// Example of a basic predicate
let searchText = "Beach"
let basicPredicate = NSPredicate(format: "name CONTAINS[cd] %@", searchText)

// Example of a compound predicate
let compoundPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: [
    NSPredicate(format: "name CONTAINS[cd] %@", searchText),
    NSPredicate(format: "destination CONTAINS[cd] %@", searchText)
])

// Example of using an expression in a predicate
let ongoingPredicate = NSPredicate(format: "startDate <= %@ AND endDate >= %@", Date() as NSDate, Date() as NSDate)
let bucketListExpression = NSExpression(forFunction: "count:", arguments: [NSExpression(forKeyPath: "bucketListItems.isinplan == false")])
let bucketListPredicate = NSPredicate(format: "%@ > 0", bucketListExpression)

let finalPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [ongoingPredicate, bucketListPredicate])

For more details, you can refer to the session What’s new in SwiftData at the 10:18 mark.