Search code examples
swiftswiftdata

SwiftData filtering Query with computed property


I have a simple Item model


@Model
class Item: Identifiable, Hashable {
    @Attribute(.unique) let id = UUID()

    var start: Date?
    var end: Date?
    var isEnded: Bool {
        guard let end else { return false }
        return end < Date.now
    }

    init(start: Date?, end: Date?) {
        self.start = start
        self.end = end
    }
}

and a following view:

struct ItemsView: View {
    @Query(
        filter: #Predicate { $0.isEnded },
        sort: [SortDescriptor(\Item.start)],
        animation: .bouncy
    )
    private var items: [Item]

However there's a crash every time i try to fetch data.

Thread 1: Fatal error: Couldn't find \Item.isEnded on Item with fields [SwiftData.Schema.PropertyMetadata(name: "id", keypath: \Item.id, defaultValue: Optional(6EB32774-0C93-4DB6-B338-DBB534CAE1F4), metadata: Optional(Attribute - name: , options: [unique], valueType: Any, defaultValue: nil, hashModifier: nil)), SwiftData.Schema.PropertyMetadata(name: "start", keypath: \Item.start, defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "end", keypath: \Item.end, defaultValue: nil, metadata: nil), 
[...]

Can't computed properties be used to create predicate for @Query?


Solution

  • Computed properties can't be used in a query, the query is executed against the database so only stored properties are allowed.

    You could write the predicate without the computed property though

    private static var now: Date { Date.now }
    @Query(filter: #Predicate { 
        if $0.end == nil {
            return false
        } else {     
            return $0.end! < now 
        }
    }, sort: [SortDescriptor(\Item.start)], animation: .bouncy)
    

    Note that we can't call Date.now directly in the predicate but we can use a variable that contains the value instead.