Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can we use custom structs/enums with the Model and Predicate macros?

Swift 5.9 and the new SwiftData framework introduce the @Model and #Predicate macros. We can now use custom enums and structs in our our models, like so:

@Model
final class Item {
    var name: Name
    var nature: Nature

    struct Name: Codable {
        let base: String
        let suffix: String?
    }

    enum Nature: Int, Codable {
        case foo = 0
        case bar = 1
        case baz = 2
    }

    init(name: Name, nature: Nature) {
        self.name = name
        self.nature = nature
    }
}

But how can we use them in a Predicate? All of these examples fail:

// Member access without an explicit base is not allowed in this predicate (from macro 'Predicate')
let predicate = #Predicate<Item> { $0.nature == .foo }

// Cannot infer key path type from context; consider explicitly specifying a root type.
let predicate = #Predicate<Item> { $0.nature.rawValue == Item.Nature.foo.rawValue }

// No compile error, but causes the Xcode Preview to crash when used inside a @Query.
let predicate = #Predicate<Item> { $0.name.base.contains("Hello, world!") }

// Optional chaining is not supported here in this predicate. Use the flatMap(_:) function explicitly instead. (from macro 'Predicate').
let predicate = #Predicate<Item> { $0.name.suffix?.contains("Hello, world!") ?? false }
like image 912
parapote Avatar asked Dec 19 '25 06:12

parapote


1 Answers

You need to capture/freeze the comparison value outside of the predicate closure:

let foo = Item.Nature.foo
let predicate = #Predicate<Item> { $0.nature == foo }

Not sure about the name issue, but it'd probably be easier if Name was a @Model itself and a @Relationship instead of a Codable.

Beware with Xcode 26! If your attribute is optional, you now need to cast the captured value to an optional, so something like this:

let foo: String? = Item.Nature.foo
let predicate = #Predicate<Item> { $0.nature == foo }
like image 177
Arnaud Avatar answered Dec 22 '25 22:12

Arnaud



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!