Search code examples
swiftuiswiftdata

SwiftData - Cannot convert value of type 'PredicateExpressions.Equal<PredicateExpressions.KeyPath<PredicateExpressions.Variable<Array<Todo>.Element>


I receive the below error when trying to init a SwiftData query in a view:

 Error: Cannot convert value of type 'PredicateExpressions.Equal<PredicateExpressions.KeyPath
<PredicateExpressions.Variable<Array<Todo>.Element>, Int>, PredicateExpressions.KeyPath<PredicateExpressions.Value<Section>, 
Int>>'(aka'PredicateExpressions.Equal<PredicateExpressions.
KeyPath<PredicateExpressions.Variable<Todo>, Int>, PredicateExpressions.KeyPath<PredicateExpressions.Value<Section>, 
Int>>') to closure result type 'any StandardPredicateExpression<Bool>'

TodoView.swift

import SwiftUI
import SwiftData

struct TodoView: View {
    
    @Query var todos: [Todo]
    
    @EnvironmentObject var currentSection: Section
    
    @Environment(\.modelContext) var modelContext
    
    var DWMY = ["D","W","M","Y"]
    
    var body: some View {
        List {
            ForEach(todos) { todo in
                HStack {
                    if todo.completed == false {
                        Text("[ ]")
                    } else {
                        Text("[X]")
                    }
                    Text(todo.name)
                    Text(DWMY[todo.section])
                }
            }
            .onDelete(perform: deleteToDo)
        }
        .navigationBarTitleDisplayMode(.inline)
    }
    
    init(todoSection: Section) {
        _todos = Query(filter: #Predicate { todo in
            todo.section == currentSection.section
        })
    }
    
    func deleteToDo(at offsets: IndexSet ) {
           for offset in offsets {
               let todo = todos[offset]
               modelContext.delete(todo)
           }
    }
}

The error occurs when I try to give the query the value currentSection.section if I replace currentSection.section with a hard coded Int there are no errors.

From what I have read this is happening because my Environment is not passed down to ToDoView.swift until Body is created so currentSection.section does not exist when init occurs.

What is the correct way to pass my currentSection.section value into init?


Solution

  • I would make TodoView not depend on the environment object for creating the Query. Have the init take in the section number.

    struct TodoView: View {
        @Query var todos: [Todo]
    
        // you could still have the EnvironmentObject here for other purposes, just not for creating the query
        // @EnvironmentObject var currentSection: Section
        
        init(sectionNumber: Int) {
            _todos = Query(filter: #Predicate { todo in
                        todo.section == sectionNumber
                    })
        }
        
        var body: some View {
            List(todos) { todo in
                Text(todo.name)
                // ...
            }
        }
    }
    

    Then get the EnvironmentObject in the parent view:

    struct TodoWrapper: View {
        @EnvironmentObject var currentSection: Section
    
        var body: some View {
            TodoView(sectionNumber: currentSection.section)
        }
    }