Home > Software engineering >  Why alert dialog delete the wrong item in SwiftUI?
Why alert dialog delete the wrong item in SwiftUI?

Time:03-01

I have a problem in my SwiftUI project, and I am trying to use alert dialog to delete an item in a list, but when use the .alert, wrong item gets deleted. I am still do not know what I missed?

Here is my code:

struct CustomView: View {
    @State private var selectedUsers: CustomModel?
    @State var users: [CustomModel]
    @State private var selectDelete = false
    var body: some View {
        ScrollView(.vertical, showsIndicators: false, content: {  
            VStack(content: {
                ForEach(users){ user in
                    CustomRowView(user: user)
                        .contextMenu {
                            Button(action: {
                                self.delete(item: data)
                            }) {
                                Text("remove") 
                            }
                        }
                        .onTapGesture {
                            selectedUsers = user
                        }
                        .alert(isPresented: $selectDelete) {
                            Alert(title: Text("title"),
                                message: Text("message"),
                                primaryButton: .destructive(Text("Delete")) {
                                    self.delete(item: user)
                                },
                                secondaryButton: .cancel()
                            )
                        }
                        .onDelete { (indexSet) in
                            self.users.remove(atOffsets: indexSet)
                        }
                }
            })
        })
    }

    private func delete(item user: CustomModel) {
        if let index = users.firstIndex(where: { $0.id == user.id }) {
            users.remove(at: index)
        }
    }
}

model:

struct CustomModel: Identifiable{
var id = UUID().uuidString
var name: String
}

var users = [

CustomModel(name: "david"),
CustomModel(name: "marry"),
CustomModel(name: "henry"),
CustomModel(name: "nadi"), ]

CodePudding user response:

There are 2 problems in your code that are creating the unexpected behaviour:

  1. When you iterate through the user with ForEach, you are attaching one .alert() modifier to each single instance. This means, when selectDelete is set to true, all of the instances try to show an alert, but only one will. Which one? Who knows, but that instance will be deleted.

  2. You have a nice selectedUsers variable that changes when you tap on it. But you are deleting the user, not selectedUser.

How you can fix your code to work with .alert() in 3 steps:

  • if you don't need to perform any task with the tap gesture, just delete it and change the selectedUser in your context menu:
.contextMenu {
    Button(action: {

    selectedUsers = user    // Change here then delete

    self.delete(item: data)
}) {
    Text("remove") }}

    // Forget about it...
    //.onTapGesture {
    //    selectedUsers = user
    //}
  • attach your alert to the top-most level of your view (at the bottom):
ScrollView {
    ...
}
.alert(isPresented: $selectDelete) {
    ...
}
  • delete selectedUser, not user:
self.delete(item: selectedUser)
  • Related