0

I have a DataModel with the following struct:

struct Task: Codable, Hashable, Identifiable {
    var id = UUID()
    var title: String
    var completed = false
    var priority = 2
}

In one my views, I populate a list with buttons based on each element of that DataModel, essentially a Task.

I pass each task for the view like this:

ForEach(taskdata.filteredTasks(byPriority: byPriotity), id: \.id) { task in
           TaskView(task: task)
}

On the view, I add each button as follow:

struct TaskView: View {
    @EnvironmentObject private var taskdata: DataModel
    @State var task: Task
    
    var body: some View {
        Button(action: {
            task.completed.toggle() }) {
         
            ....
         }
    }
}

Now, when the user clicks on the button, if completed was false, it makes it true. Simple. However, this doesn't update the DataModel, so I added the following to see if I could find the item in question and update it:

struct TaskView: View {
    @EnvironmentObject private var taskdata: DataModel
    @State var task: Task
    @State var id: UUID // added this so I can also pass the ID and try to find it
    
    var body: some View {
        Button(action: {
            task.completed.toggle()
            if task.completed { // if it's completed, find it an update it.
                //taskdata.tasks.first(where: { $0.id == id })?.completed = true
                //taskdata.tasks.filter {$0.id == id}.first?.completed = true
                
                /*if let index = taskdata.tasks.first(where: {$0.id == id}) {
                    print(index)
                    taskdata.tasks ????
                }*/
            }
        }) {
             ....
           }

All the commented code is what I have tried... I get different errors, from Cannot assign to property: function call returns immutable value to cannot assign to property: 'first' is a get-only property and a few other colorful Xcode errors.

How do I find the correct object on my DataModel and correctly update its .completed to true (or false if it's clicked again).

Thank you

2
  • 1
    You have an answer for your question but I would advice looking into declaring your DataModel as an observable object and declaring taskdata as a @StateObject in the top view and task as a @Binding in the subview so that the DataModel would get updated directly Commented Oct 4, 2021 at 12:07
  • This is the better advice. @State vars are ONLY internal to the view. By declaring your model as an ObservableObject, and then use it as @StateObject, you gain the benefits of State While directly updating your model. You can also make your model into what is called a Singleton, so that you have one source for data across the entire app. That is referred to as a "Single Source of Truth". Commented Oct 4, 2021 at 12:31

2 Answers 2

3

You need to use Array >>> firstIndex

if let index = taskdata.tasks.firstIndex(where: {$0.id == id}) { 
  taskdata.tasks[index].completed = true
}
Sign up to request clarification or add additional context in comments.

1 Comment

Ah... That makes sense. Thank you, I'm still trying to figure out the idiosyncrasies of Swift.
1

There is another way for your problem using a function in extension like this custom update function:

extension Array {

    mutating func update(where condition: (Element) -> Bool, with newElement: (Element) -> Element) {
        
        if let unwrappedIndex: Int = self.firstIndex(where: { value in condition(value) }) {
            
            self[unwrappedIndex] = newElement(self[unwrappedIndex])
            
        }
  
    }
    
}

use case:

taskdata.tasks.update(where: { element in element.id == id }, with: { element in
    
    var newElement: Task = element
    
    newElement.completed = true
    
    return newElement
 
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.