How to ForEach over bindable collections?

Lets say I have a list of todo items. And a button that adds an item to the list:


VStack(alignment: .leading) {
  Button(action: {...}) {
    Text("Add Item")
  }
  Divider()
  ForEach(...) {...}
}


If I have a simple Todo value and pass in Binding of an array of them, I can fill in some of the gaps:


@Binding var todos: [Todo]

VStack(alignment: .leading) {
  Button(action: { self.todos.append(Todo(title: "new thing")) }) {
    Text("Add Item")
  }
  Divider()
  ForEach(todos) { todo in
    HStack {
      Button(action: { todo.isComplete.toggle() }) { // ERROR!
        Image(systemName: todo.isComplete ? "checkmark.square.fill" : "square")
      }
      Text(todo.title)
    }
  }
}


This almost works but for the error at line 10. `Binding<[Todo]>` doesn't survive passage through ForEach. The `todo` that comes out the other side is just a regular old `Todo`. So I can't mutate it.


The best way I've seen so far to get around this is to use an index with the ForEach, instead:


@Binding var todos: [Todo]
ForEach(model.todos.indices) { i in
  HStack {
    Button(action: { self.todos[i].isComplete.toggle() }) {
      Image(systemName: self.todos[i].isComplete ? "checkmark.square.fill" : "square")
    }
    Text(self.todos[i].title)
  }
}


This works great! Tapping on the button checks my boxes like magic! But if I try to add a todo, things go wonky or crash because (I think?) ForEach uses the index to ID views across updates — and adding, removing, or reordering elements in the array will cause different indices to be associated with the same content?


So it seems we can either mutate our elements or add/remove them, not both?? This has to be a fairly common interaction, right? I'm clearly missing something obvious?

I tried to reproduce, but I may miss some context.


Could you show the complete code, with Todo definition ?


Did you try to use

ForEach(todos, id: \.self)

How to ForEach over bindable collections?
 
 
Q