HomeiOS Developmentios - SwiftData runtime crash utilizing Predicate macro with protocol-based generic mannequin

ios – SwiftData runtime crash utilizing Predicate macro with protocol-based generic mannequin


I am working with SwiftData and attempting to share logic throughout a number of fashions utilizing protocols and protocol extensions.

I’ve created some widespread protocols like Queryable, StatusRepresentable, and Trackable, which my SwiftData fashions (e.g., Pet) conform to.

My mannequin seems to be like this:

@Mannequin
ultimate class Pet {
    var id: UUID
    var identify: String
    var statusRaw: String
    // ... different properties
}

And I outline these protocols:

protocol StatusRepresentable: AnyObject, PersistentModel {
    var statusRaw: String { get set }
}

extension StatusRepresentable {
    var standing: Standing {
        get { Standing(rawValue: statusRaw) ?? .lively }
        set { statusRaw = newValue.rawValue }
    }

    func changeStatus(to newStatus: Standing) {
        if newStatus != standing {
            self.updateTimestamp(onChange: newStatus)
            self.statusRaw = newStatus.rawValue
        }
    }
}

And:

protocol Queryable: AnyObject, Identifiable, StatusRepresentable, PersistentModel {}

extension Queryable {
    static var activePredicate: Predicate {
        .withStatus(.lively)
    }

    static func predicate(for id: UUID) -> Predicate the place Self.ID == UUID {
        .withId(id)
    }
}

Here is the problematic half:

I’m utilizing a generic predicate extension like this:

extension Predicate {
    static func withStatus(_ standing: Standing...) -> Predicate {
        let rawValues = standing.map { $0.rawValue }
        return #Predicate {
            rawValues.comprises($0.statusRaw)
        }
    }
}

Then in my SwiftUI View, I take advantage of it like so:

struct ComponentActiveList: View {
    @Question personal var activePets: [Pet]

    init() {
        self._activePets = Question(
            filter: .activePredicate, // or .withStatus(.lively)
            kind: .identify,
            order: .ahead
        )
    }

    var physique: some View {
        // ...
    }
}

The issue:

It compiles superb, however crashes at runtime with this error (simplified):

keyPath: .statusRaw
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x...)

Within the expanded macro, I can see this:

Basis.Predicate({
    PredicateExpressions.build_contains(
        PredicateExpressions.build_Arg(rawValues),
        PredicateExpressions.build_KeyPath(
            root: PredicateExpressions.build_Arg($0),
            keyPath: .statusRaw
        )
    )
})

It looks as if the macro is having hassle resolving .statusRaw through protocol extension / dynamic lookup. I am guessing this has one thing to do with SwiftData + `#Predicate being unable to resolve protocol-constrained properties at runtime?


Earlier than introducing protocols like Queryable and StatusRepresentable, I had this working by duplicating the predicate logic for every mannequin individually – for instance:

extension Predicate {
    static func pets(with standing: Standing...) -> Predicate {
        let rawValues = standing.map { $0.rawValue }
        return #Predicate {
            rawValues.comprises($0.statusRaw)
        }
    }

    static func pet(with id: UUID) -> Predicate {
        #Predicate { $0.id == id }
    }
}

As a workaround, I’ve at present reverted all of the protocol code and am duplicating the predicate logic for every mannequin straight. However ideally, I’d wish to outline these in a single place through protocols or generics.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments