39

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!

ardevd
  • 3,329
  • 5
  • 30
  • 55
  • 3
    the API is the same so the examples you've seen are valid - try it and shown code if you have a problem – Wain Feb 08 '16 at 08:51

3 Answers3

68

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"

pkamb
  • 33,281
  • 23
  • 160
  • 191
Manuel
  • 1,675
  • 12
  • 18
  • But what if messageKeyPredicate in this example, only exists in certain situations. Is there an elegant way of avoiding spagetti code in trying to construct the NSCompoundPredicate? – ardevd Feb 08 '16 at 09:20
  • Nevermind, just building a list of NSPredicates is fine! Thank you! – ardevd Feb 08 '16 at 12:26
  • Can we use andPredicate - or what's the difference? let andPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicateIsNumber, predicateIsEnabled]) – davidrynn Nov 16 '17 at 20:07
32

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

Nic Wanavit
  • 2,363
  • 5
  • 19
  • 31
Harjot Singh
  • 6,767
  • 2
  • 38
  • 34
-1

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.

omihek
  • 53
  • 6
  • @loremipsum I wrote this answer. – omihek Mar 29 '23 at 16:05
  • I didn't flag it just put the link because I was not sure. It looks very ChatGPT generated especially because the approach uses `filter` that is very inefficient with `FetchResults` it is much more efficient to just apply the predicate to the `FetchRequest` – lorem ipsum Mar 29 '23 at 16:09
  • 1
    Just for fun I put my entire answer in ChatGPT and asked it if it wrote it. It said no. Then I put just the code in and asked if it wrote it. It said no. I also put the code in a couple of AI detectors. It said 98% chance written by a human. – omihek Mar 29 '23 at 16:11
  • I didn't flag like I said, just a comment – lorem ipsum Mar 29 '23 at 16:11
  • Oh I see my bad I didn't understand your previous comment. I'd be open to a more efficient way. This is the only way I could figure out how to do it dynamically and be able to change the predicates based on user interaction. – omihek Mar 29 '23 at 16:12
  • https://developer.apple.com/wwdc21/10017 – lorem ipsum Mar 29 '23 at 16:16
  • 1
    https://stackoverflow.com/questions/68530633/how-to-use-a-fetchrequest-with-the-new-searchable-modifier-in-swiftui/68549693#68549693 – lorem ipsum Mar 29 '23 at 16:18
  • I saw both of those. Couldn't figure out a good way of having multiple dynamic predicates. The query property deals with the searchText, but I also have a button that filters by favorited Pokemon, so I need a solution that manages and accounts for both predicates, because I can't wait for a search to be started to filter by favorites, you know? – omihek Mar 29 '23 at 16:21
  • 1
    `onChange` of the `searchText` or `filterByFavorites` `pokedex.nsPredicate = compoundPredicate`. – lorem ipsum Mar 29 '23 at 16:26
  • 1
    Nice, that actually works a lot better because the problem I was having before is changes to my list weren't animating. Not a super big deal but something I wanted a solution to. Thanks! I'll update the answer to make it better. – omihek Mar 29 '23 at 16:32