Core Data NSPredicate use with dynamic Boolean argument

In my Core Data, I have a "Car" Entity with 3 attributes:

  • id:UUID
  • make:String
  • sold:Bool

in my struct:View I have the following two Sate vars

@State private var make = "Ford"
@State private var carSold = true

This following request correctly returns all of the CarMO objects

@FetchRequest(sortDescriptors: ) var cars: FetchResults

When I try to use the predicate the filter the results on the @State var

The following NSPredicate correctly returns only the cars of make "Ford".

cars.nsPredicate = NSPredicate(format: "make = %@", make)

The following NSPredicate correctly returns only the cars that are sold but is not dynamic.

cars.nsPredicate = NSPredicate(format: "sold = true")

I have tried every possible format I could think of to reference the @State var carSold but they either fail or return all cars"

failure example:

cars.nsPredicate = NSPredicate(format: "sold = %@", carSold)

I have search the web and got nothing applicable to booleans.

I bought "SwiftUI For MasterMinds 2nd Edition 2022". Chapter 13.4 Core Data has a good section on "Predicates" but only string and number as arguments. Nothing on booleans.

I read Apple's "Predicate Programming Guide" and its referenced "Predicate Format String Syntax". The only thing I found was the following:

Boolean Value Predicates

TRUEPREDICATE

A predicate that always evaluates to TRUE

FALSEPREDICATE

A predicate that always evaluates to FALSE

I tried to see how that would be of use in my situation but again came up empty.

I would really appreciate if someone could show me how to reference a boolean in the NSPredicate format string or maybe point me to some documentation that has the answer.

Thank you.

If I understand correctly, try the following:

NSPredicate(“sold = %@“, carSold as NSNumber)

%@ is matching an objective C object. There are numeric format characters but I never remember them so I almost always fallback to this.

Thank you!!!. Your answer worked perfectly and once again, I realize how little I understand about Swift. I'm not quite sure how converting a Bool to a NSNumber and then comparing it to another Bool works, but it does.

Again thank you. You saved a very nice iMac from being thrown out a third story window.

(just kidding, I live on a ground floor) ;)

1 Like

what DA HECK!!??? it works!!

why a bool is a nSNUMBER?

NSPredicate(format: "today == %@", true as NSNumber),

All you are doing is turning the bool into an object representation for the NSPredicate format. And a bool can be represented as a number (0 or 1).

%@ is a formatter modifier that wants NSObject and you can also pass swift's String as it is bridged to NSString. NSPredicate's documentation wants TRUE/YES/FALSE/NO so I'd pass it this way:

NSPredicate(format: "today == %@", isToday ? "TRUE" : "FALSE")

and to keep the code cleaner I'd create this extension:

extension Bool {
    var string: String {
         self ? "TRUE" : "FALSE"
    }
}

NSPredicate(format: "today == %@", isToday.string)

The bridging works as expected if you use the initialiser that takes an array of arguments. Eg

NSPredicate(format: "sold == %@", argumentArray: [true])

FWIW you can avoid these problems with simpler predicates pretty easily by wrapping NSComparisonPredicate and NSCompoundPredicate in a more Swift-friendly interface. For example:

func == <T>(lhs: KeyPath<some NSManagedObject, T>, rhs: T) -> NSPredicate {
    NSComparisonPredicate(leftExpression: NSExpression(forKeyPath: lhs), rightExpression: NSExpression(forConstantValue: rhs), modifier: .direct, type: .equalTo)
}

let fetchRequest = Car.fetchRequest()
fetchRequest.predicate = \Car.sold == true
4 Likes

That's quite cool. Expanding on this:

extension NSPredicate {
    static func && (a: NSPredicate, b: NSPredicate) -> NSPredicate {
        NSCompoundPredicate(andPredicateWithSubpredicates: [a, b])
    }
    static func || (a: NSPredicate, b: NSPredicate) -> NSPredicate {
        NSCompoundPredicate(orPredicateWithSubpredicates: [a, b])
    }
    static prefix func ! (a: NSPredicate) -> NSPredicate {
        NSCompoundPredicate(notPredicateWithSubpredicate: a)
    }
}

to further allow combining predicates:

fetchRequest.predicate = \Car.sold == true && \Car.today == true
3 Likes