For a SwiftUI @FetchRequest.
Here's how to dynamically set the predicates for your fetch request. First, the properties I have in my SwiftUI view:
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Pokemon.id, ascending: true)],
animation: .default
) private var pokedex: FetchedResults<Pokemon>
@State var searchText = ""
@State var filterByFavorites = false
var compoundPredicate: NSPredicate {
var predicates: [NSPredicate] = []
// Add search predicate
if !searchText.isEmpty {
predicates.append(NSPredicate(format: "name CONTAINS[c] %@", searchText))
}
// Add favorite filter predicate
if filterByFavorites {
predicates.append(NSPredicate(format: "favorite == %d", true))
}
// Combine predicates
return NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
}
Notice I don't set any predicate in the fetch request at this point. Also notice, the part that makes this all work is the compoundPredicate
computed property. I start with an empty predicates
array, then I check my searchText
and append the appropriate predicate if the condition is true. Then I check my filterByFavorites
property and do the same.
Alright, now the code inside the body var that makes it work. I won't post my whole view since most of it is irrelevant, but here is where I add my List view which shows my pokedex fetched results:
Edit: My previous method used this filter (shown below) to evaluate each item in the list with the compoundPredicate
to show only those Pokemon that matched, but I was made aware by lorem ipsum (see answer comments) that this is an inefficient way of doing it, plus it didn't allow the list to animate the way I wanted it to. I'll show both solutions.
Old solution:
List(pokedex.filter { compoundPredicate.evaluate(with: $0) }) { pokemon in
...
}
.searchable(text: $searchText, prompt: "Find a Pokemon")
.autocorrectionDisabled()
New solution:
List(pokedex) { pokemon in
...
}
.searchable(text: $searchText, prompt: "Find a Pokemon")
.autocorrectionDisabled()
.onChange(of: searchText) { _ in
pokedex.nsPredicate = compoundPredicate
}
.onChange(of: filterByFavorites) { _ in
pokedex.nsPredicate = compoundPredicate
}
So instead of using the filter from the old solution, I just use these 2 onChange
modifiers to watch for changes to the searchText
and the filterByFavorites
properties and then I apply the compoundPredicate
to my pokedex fetched results directly.
Elsewhere in the view I have a button the user can tap to toggle filterByFavorites
between true and false.