Search code examples
swiftcore-dataswiftui

how to delete an item in a forEach loop with a delete btn for each of it. I use swiftUI with core data


Here is the code if you don't understand

struct cartView: View {
        @Environment(\.managedObjectContext) private var viewContext
    
        @FetchRequest(
            sortDescriptors: [], animation: .default) private var products: FetchedResults<Prod>
        
        let columns = [GridItem(.flexible()), GridItem(.flexible())]
        @State var indx = 0
    
        var body: some View {
            NavigationView {
                ScrollView {
                    VStack {
                        LazyVGrid(columns: columns) {
                            ForEach(products, id: \.self) {prod in
                                let prodItems = Product(name: prod.name ?? "Undefined", price: prod.price ?? "Undefined", type: "type", brand: prod.brand ?? "Undefined", images: [prod.image!,""])
                                
                                    ZStack {
                                    Cells(product: prodItems)
                                    // I want the button below delete the item of the 
                                    // button pressed with the function below
                                    Button(action: {}) {
                                        Image(systemName: "xmark.bin.circle")
                                            .resizable()
                                            .frame(width: 30, height: 30)
                                            .foregroundColor(.red)
                                            .background(.black)
                                            .clipShape(RoundedRectangle(cornerRadius: 20))
                                            .offset(x: 60, y: 45)
                                    }
                                }
                            }
                        }
                    }
                }
                .navigationTitle("Cart")
            }
        }
        
        private func deleteItems(offsets: IndexSet) {
            withAnimation {
                offsets.map { products[$0] }.forEach(viewContext.delete)
    
                do {
                    try viewContext.save()
                } catch {
                    let nsError = error as NSError
                    fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
                }
            }
        }
}

so the question here is the button there in the forEach loop to delete the item that the user pressed the button and I know how to do it, but what I don't know is how to get the index of the item and pass it in the function.


Solution

  • You don't actually need the index if you're issuing the delete instruction from within your loop, as your NSManagedObjectContext instance has a delete(_:) method that takes the object itself. That change will propagate through your @FetchRequest object automatically, your SwiftUI view will update to show the collection without the now-deleted object.

    So your button action becomes:

    Button(action: {
      viewContext.delete(prod)
    }) {
      Image(systemName: ...)
      // etc.
    }
    

    Note that while you'll see the effect straight away, the deletion will only be in memory until you call save on the managed object context.

    In my CoreData apps, I tend to save my changes separately, for example when the app is about to go into the background. But if you want to trigger a save immediately the object is removed, that's straightforward enough:

    Button(action: {
      viewContext.delete(prod)
      try? viewContext.save()
    }) {
      Image(systemName: ...)
      // etc.
    }
    

    NB: the documentation for NSManagedObjectContext.save() says that you should check the hasChanges property before attempting to save, but as you've just made a change in the line above, that's not necessary in this particular example.