Search code examples
arraysswiftuilazyvstack

SwiftUI: Index out of range when last element gets removed


I have a checklist made with LazyVStack, I implemented remove functionality for the checklist items, but for some reason the app crashes with "Index out of range" only when I try to remove the last element

Here is my code:

struct ChecklistView: View {
    // Properties
    // ==========
    @ObservedObject var checklist = Checklist()
    @ObservedObject var viewModel: ChecklistViewModel
    @Binding var checklistItems: [ChecklistItem]
    @State var newItemName = ""
    @State var newChecklistItemViewIsVisible = false
    @State var presentAddNewItem = true
    let offlineMode: Bool

    var body: some View {
        VStack {
            LazyVStack {
                ForEach(checklistItems) { item in
                    HStack {
                        RowView(checklistItem: $checklistItems[item], viewModel: viewModel)
                            .listRowInsets(.init(top: 0, leading: 8, bottom: 0, trailing: 0))
                            .padding(.horizontal, 12)
                            .padding(.top, 12)
                            .padding(.bottom, 4)

                        Button {
                            //Prints the correct index number for the last element in the array, but when I remove the last element, always crashes.
                            print(index)
                            if let index = checklistItems.firstIndex(where: {$0.id == checklistItems[item].id}){
                                checklistItems.remove(at: index)
                            }
                        } label: {
                            Text("X")
                        }
                    }

                    Divider()
                        .frame(width: 311)
                }
            }
        }
        .frame(width: UIScreen.main.bounds.width - 32)
        .background(backgroundSecondary)
        .cornerRadius(16)
        .overlay(
            RoundedRectangle(cornerRadius: 16)
                .stroke(borderGray, lineWidth: 1)
        )
    }
}

Solution

  • I found a solution, I changed my forEach to:

    ForEach(checklistItems.indices, id: \.self) { index in
                        HStack {
                            RowView(
                                checklistItem:
                                    Binding(
                                        get: { self.checklistItems[index] },
                                        set: { self.checklistItems[index] = $0 }
                                    ),
                                viewModel: viewModel
                            )
                            .padding(.leading, 12)
    
                            if checklistEditMode {
                                Button {
                                    checklistItems.remove(at: index)
                                } label: {
                                    Image("XCircle")
                                        .resizable()
                                        .frame(width: 18, height: 18)
                                }
                                .padding(.trailing, 12)
                            }
                        }
                        .padding(.horizontal, 12)
                        .padding(.top, 12)
                        .padding(.bottom, 4)
    
                        Divider()
                            .frame(width: 311)
                    }
    

    and it works now with no crashes, Thanks to this