I can't understand why the error occurs. The addItem()
works, but an error occurs when removeItem()
is executed.
MyViewModel
class MyViewModel: ObservableObject {
@Published var items:[MyModel] = []
}
MyModel
struct MyModel : Identifiable {
var id: Int
var title:String
var value:String
}
ContentView
struct ContentView: View {
@StateObject var model = MyViewModel()
var body: some View {
VStack {
Button(action: {
addItem()
}) {
Text("add")
}
ForEach(0..<model.items.count, id: \.self) { index in
HStack {
TextField("value", text: Binding(get: { self.model.items[index].value },
set: { self.model.items[index].value = $0 }))
Button(action: {
removeItem()
}) {
Text("delete")
}
}
}
}
}
func addItem() {
self.model.items.append(MyModel(id: +1, title: "", value: ""))
}
func removeItem() {
if let index = model.items.first?.id {
model.items.remove(at: index)
}
}
}
I think the problem is in func removeItem()
The problem is in ForEach
As mention in the apple doc
ForEach
: The instance only reads the initial value of the provided data and doesn’t need to identify views across updates.
So when you remove an object from the array, you have changed the array but SwiftUI doesn't see that change and it uses the original array.
So, you just fix it by just adding conditions like this index < self.model.items.count ? self.model.items[index].value : ""
Also, I suggest using .indices
in ForEach
One more point you no need to find index every time on delete. Just pass the index to function.
Here is the complete code.
struct ContentView: View {
@StateObject var model = MyViewModel()
var body: some View {
VStack {
Button(action: {
addItem()
}) {
Text("add")
}
ForEach(model.items.indices, id:\.self) { index in //< -- Here
HStack {
TextField("value", text: Binding(get: { index < self.model.items.count ? self.model.items[index].value : "" }, //<-- Here
set: { self.model.items[index].value = $0 }))
Button(action: {
removeItem(at: index) //< -- Here
}) {
Text("delete")
}
}
}
}
}
func addItem() {
self.model.items.append(MyModel(title: "", value: ""))
}
func removeItem(at index: Int) {
model.items.remove(at: index) //< -- Here
}
}