ardevd
ardevd

Reputation: 3407

Multiple NSPredicates for NSFetchRequest in Swift?

Currently, I have a simple NSFetchRequest with an associated NSPredicate. However, Im hoping there is a way you can append multiple predicates. I've seen examples in Objective C, but none for Swift.

Can you define a list of NSPredicate's or append multiple NSPredicate objects to a single NSFetchRequest somehow?

Thanks!

Upvotes: 39

Views: 19474

Answers (3)

omihek
omihek

Reputation: 83

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.

Upvotes: -1

Manuel
Manuel

Reputation: 1695

You can use "NSCompoundPredicate". For example:

let converstationKeyPredicate = NSPredicate(format: "conversationKey = %@", conversationKey)
let messageKeyPredicate = NSPredicate(format: "messageKey = %@", messageKey)
let andPredicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: [converstationKeyPredicate, messageKeyPredicate])
request.predicate = andPredicate

You can change into "AndPredicateType" or "OrPredicateType"

Upvotes: 68

Harjot Singh
Harjot Singh

Reputation: 6927

Update for Swift 4

let predicateIsNumber = NSPredicate(format: "isStringOrNumber == %@", NSNumber(value: false))
let predicateIsEnabled = NSPredicate(format: "isEnabled == %@", NSNumber(value: true))
let andPredicate = NSCompoundPredicate(type: .and, subpredicates: [predicateIsNumber, predicateIsEnabled])

//check here for the sender of the message

let fetchRequestSender = NSFetchRequest<NSFetchRequestResult>(entityName: "Keyword")
fetchRequestSender.predicate = andPredicate

The change in latest Swift Version is:

`NSCompoundPredicateType.AndPredicateType` replaced by `NSCompoundPredicate.LogicalType.and`

Hope it helps!!!

Thanks

Upvotes: 32

Related Questions