SwiftData query filter on an @State var

Hey, I might have a super dumb question but I am very new to SwiftData and Swift in general. So I have the following code in a View:

@State private var selectedMonth: String = ""
@State private var selectedYear: String = ""

@Query(
   filter: #Predicate<Transaction> { transaction in 
transaction.date.monthString == selectedMonth && transaction.date.yearString == selectedYear
     },
   sort: \Transaction.date
) var transactions: [Transaction]

My @State vars selectedMonth and selectedYear get changed whenever the user selects options from a menu. I've had trouble getting this query/filter to work, does anyone know if filtering on dynamic (@State) variables is supported in SwiftData? If so, am I missing something here?

I've also tried the pattern of having the transactions array live elsewhere in a regular swift file with the @Published macro and calling an update function whenever the @State vars selectedMonth or selectedYear get updated using .onChange(of:) . Unfortunately, I've not been able to find any documentation or examples about setting up a regular swift file (not SwiftUI) to work with SwiftData (e.g. querying from it, accessing the context/container, etc)

Would appreciate anyone giving tips or pointing me in the right direction. Sorry if its a noob questions, thanks so much for any help

You cannot use any instance member inside a Predicate so this shouldn't even compile

struct MyView: View {
    @State private var selectedMonth: String = ""

    // Instance member 'selectedMonth' cannot be used on type 'MyView'; did you mean to use a value of this type instead?
    @Query(filter: #Predicate<Transaction> { $0.date.monthString == selectedMonth })
    var transactions: [Transaction]
}

You can achieve what you want by putting your Query into a subview where you can provide a predicate or sort value using dependency injection.

struct ContainerView: View {
    @State private var selectedMonth: String = ""
    @State private var selectedYear: String = ""

    var body: some View {
        TransactionList(month: selectedMonth, year: selectedYear) // called every time your state properties change
            .toolbar {
                 Menu { ... } // update your states values
            }
    }
}

struct TransactionList: View {
    @Query private var transactions: [Transaction]

    init(month: String, year: String) {
        self._transactions = Query(filter: #Predicate<Transaction> {
            $0.date.monthString == month && $0.date.yearString == year
        }, sort: \.date)
    }
}

For your information, you can retrieve SwiftData models by using FetchDescriptor.

let fetchDescriptor = FetchDescriptor<Transaction>(predicate: #Predicate { ... }, sortBy: [SortDescriptor(\.date)])
do {
    let transactions = try modelContext.fetch(fechDescriptor)
} catch {
}

You can find more examples in the official documentation like Preserving your app’s model data across launches

We currently can't change the filter/predicate of the Query but hopefully in the future we would be able to do something like:

@State private var selectedMonth: String = ""
@State private var selectedYear: String = ""

let query = Query<Transaction, [Transaction]>()

var transactions: [Transaction] {
    query.descriptor.predicate = #Predicate<Transaction> { transaction in 
transaction.date.monthString == selectedMonth && transaction.date.yearString == selectedYear
     }
    query.sort = \Transaction.date
    return query.wrappedValue
}

This pattern, of changing the predicate lazily when the results are requested makes more sense to me than trying to re-init the property wrapper with different params.

Currently descriptor is a private property of Query for some reason.

Feedbacks FB12367164, FB12292784

SwiftData query filter on an @State var
 
 
Q