Search code examples
swiftswiftui

SwiftUi I can not delete item


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()


Solution

  • 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
        }
    }