Delete item from a section list on SwiftUI

Viewed 2968

Overview

im doing a simple app with core data I have two entity users and territory the app shows a list of the users in sections by territory the problem is In the delete action the list delete the user from the first section if I try to delete the second user from the second section it delete the second user from the first section.

I think index set is getting wrong sending the index of the section but when I try to change the onDelete to my nested forEach don't work

Here is the code

import SwiftUI

struct ContentView: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: User.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \User.name, ascending: true)]) var users: FetchedResults<User>
    @FetchRequest(entity: Territory.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Territory.name, ascending: true)]) var territories: FetchedResults<Territory>
    @State private var showAddUser = false


       var body: some View {
           GeometryReader{ geometry in
               NavigationView {
                   ZStack {

                       List {
                           ForEach(self.territories, id: \.self) { territorie in
                            Section(header: Text(territorie.wrappedName)) {
                                ForEach(territorie.usersArray, id: \.self) { user in
                                    NavigationLink(destination: UserView(user: user)) {
                                        VStack{
                                            HStack{
                                               Text("user")
                                                Spacer()
                                                Text(user.dayLastVisit)
                                                    .padding(.horizontal)
                                            }
                                            HStack {
                                                Text(user.wrappedEmoji)
                                                    .font(.largeTitle)
                                                VStack(alignment: .leading) {
                                                    Text("\(user.wrappedName + " " + user.wrappedLastName)")
                                                        .font(.headline)
                                                    Text(user.wrappedType)

                                                }
                                                Spacer()
                                            }
                                        }

                                    }
                                }.onDelete(perform: self.deleteItem)
                            }
                           }



                       }
                       .listStyle(GroupedListStyle())
                       .environment(\.horizontalSizeClass, .regular)

                       VStack {
                           Button(action:{ self.showAddRUser.toggle()}){
                               ButtonPlus(icon:"plus")}
                           .offset(x: (geometry.size.width * 0.40), y: (geometry.size.height  * 0.38))
                           .sheet(isPresented: self.$showAddUser){
                               NewUserView().environment(\.managedObjectContext, self.moc)
                                  }
                       }
                   }
               .navigationBarTitle("Users")
                   .navigationBarItems( trailing: HStack {
                    EditButton()
                    Button(action:{self.showAddUser.toggle()}){
                    ButtonNew(text:"Nueva")}
                    }

                    .sheet(isPresented: self.$showAddUser){
                        NewUserView().environment(\.managedObjectContext, self.moc)
                   }
                   )

               }
           }
       }
    func deleteItem(at offsets: IndexSet) {
        for offset in offsets { 
            let user = users[offset]

            //borarlo del context
            moc.delete(user)

        }
        try? moc.save()
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

im learning swift and swiftui so im would appreciate any help

3 Answers

You’ll need to pass in a section index as well as the row index, so that you know which nested item to delete. Something like this.

.onDelete { self.deleteItem(at: $0, in: sectionIndex) }

And change your function to accept that section index:

func deleteItem(at offsets: IndexSet, in: Int)

In your case you can probably pass in something like territorie.id as the section index, and use that to delete the correct item. Or pass in the territorie object - whatever you need to get to the correct user. Only the index won’t get you there. Hope it all makes sense!

So thanks to the help of Kevin Renskers who found a solution. I just add a .onDelete { self.deleteItem(at: $0, in: territorie)} to my function then I use the same arrayUsers from the territory.

 func deleteItem(at offsets: IndexSet, in ter: Territory) {
        
        for offset in offsets {
             let user =  ter.usersArray[offset] 
            moc.delete(user)
            
        }
        try? moc.save()
    }

For me, the solution was the following:

ForEach(self.territories, id: \.self) { territorie in

    Section(header: Text(territorie.wrappedName)) {

        ForEach(territorie.usersArray, id: \.self) { user in
            // your code here
        }
        .onDelete { indexSet in
            for index in indexSet {
                moc.delete(territorie[user])
            }
                
            // update the view context
            moc.save()
        }
    }
}

The index in indexSet returns the item that should be deleted in that specific section. So if I delete the first item of a section, it returns 0.

The territorie returns a list of all the items that are contained in that section. So using territorie[index] will return the specific user object you want to delete.

Now that we have the object we want to delete, we can pass it to moc.delete(territorie[index]). Finally, we save it with moc.save().

Sidenote: although Misael used the variable 'territorie', I prefer to use the variable name section.

Related