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?