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

Issue

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.

Answered By – Scott Matthewman

Answer Checked By – Terry (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *