Search code examples
swiftuixcode13

SwiftUI .onDelete wrong index when list is filtered


here is my code:

import SwiftUI

struct IngredientsList: View {
    @EnvironmentObject var ingredientsStore: IngredientEdit
    @State private var searchText = ""
    
    var searchResults: [Ingredient] {
        if searchText.isEmpty {
            return ingredientsStore.items
        } else {
            return ingredientsStore.items.filter {
                $0.name.lowercased().contains(searchText.lowercased()) }
        }
    }
    
    var body: some View {
        NavigationView {
            List {
                ForEach (searchResults) { ingredient in
                    NavigationLink (destination: IngredientDetail(ingredient: ingredient)) {
                        IngredientRow(ingredient: ingredient)
                    }
                }
                .onDelete(perform: deleteItems)
            }
            .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .automatic))
            .navigationTitle("Ingredients")
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
    
    func deleteItems(at offsets: IndexSet) {
        withAnimation {
                ingredientsStore.items.remove(atOffsets: offsets)

        }
    }
    
}

Works well if searchText is empty but doesn't work if the list is filtered. Debugger shows that 'offsets' always contains indices from the filtered list (what I see on the screen), for example it will be ingredientsStore.items[1] but .remove method will pass that [1] into an ObservedObject where [1] will correspond to a completely different item and therefore a wrong item will be deleted from the array :( In other words filtered list contains indices with absolute values, as I understand it. Your heal will be much appreciated. Thank you.


Solution

  • ingredientsStore.items.remove(atOffsets: offsets)
    

    here offsets is index of searchResults.

    so you should get Ingredient items from searchResults, then delete it from ingredientsStore.items。

    try this:

    for offset in offsets{
        if let index = ingredientsStore.items.firstIndex(searchResults[offset]) {
            ingredientsStore.items.remove(at: index)
        }
    }