Search code examples
iosswiftswiftuiswiftui-listswiftui-foreach

SwiftUI OnDelete crashes using indices and bindings


I'm building an app where you have cards for each study item that is stored in an array and what I wanted to do was to allow the user to swipe left on a study item and allow them to delete the card.
Each card has two bindings to allow user to edit two textFields, so I have to use indices on the ForEach to specify the textField that is being edited, because when I don't use indices and have the foreach parameter as a binding and iterate through each item, when the user edits a specify textField, he can only type in one letter and it skips to the next textField.

In this case, present below, I am able to swipe left on each card("Section") and delete it when it's empty but when I start typing on individual textFields with different texts and delete it, it crashes.

Any help will be appreciated!

Here is the foreach loop that is in a subview of another view

@ObservedObject var currentStudySet: HomeViewModel

ForEach(currentStudySet.studySet.studyItem.indices, id: \.self) { index in
    Section {
        VStack {
            TextField("Title", text: $currentStudySet.studySet.studyItem[index].itemTitle)
            .padding(5)
            .frame(maxWidth: .infinity, alignment: .leading)
            .background(backgroundColor)
            .cornerRadius(10)

            TextField("Description", text: $currentStudySet.studySet.studyItem[index].itemDescription)
            .padding(5)
            .frame(maxWidth: .infinity, alignment: .leading)
            .background(backgroundColor)
            .cornerRadius(10)
        }
        .frame(maxWidth: .infinity)
    }
}
.onDelete(perform: { (item) in
    currentStudySet.studySet.studyItem.remove(atOffsets: item)
})

The view model has studySet but I won't put it for legibility purposes but here is the StudyModel that is "studySet"

struct StudyModel: Hashable{
    var title: String = ""
    var days = ["One day", "Two days", "Three days", "Four days", "Five days", "Six days", "Seven days"]
    var studyGoals = "One day"
    var studyItem: [StudyItemModel] = []
}

Here is the studyItemModel which is the studyItem array in StudyModel

struct StudyItemModel: Hashable{
    var itemTitle: String = ""
    var itemDescription: String = ""
}


Solution

  • You can avoid using the index by doing the model conforms to Identifiable, this is the way the ForEach is meant to be use in SwiftUI atleast

    import Foundation
                    import SwiftUI
    
                    struct fortest: View {
                        
                        @ObservedObject var currentStudySet: HomeViewModel = HomeViewModel()
                        
                        var body: some View {
                            
                            List {
                                ForEach($currentStudySet.studySet.studyItem) { $studyItem in
                                    Section {
                                        VStack {
                                            TextField("Title", text: $studyItem.itemTitle)
                                                .padding(5)
                                                .frame(maxWidth: .infinity, alignment: .leading)
                                                .background(Color.gray)
                                                .cornerRadius(10)
                                            
                                            TextField("Description", text: $studyItem.itemDescription)
                                                .padding(5)
                                                .frame(maxWidth: .infinity, alignment: .leading)
                                                .background(Color.gray)
                                                .cornerRadius(10)
                                        }
                                        .frame(maxWidth: .infinity)
                                    }
                                }
                                .onDelete(perform: { (item) in
                                    currentStudySet.studySet.studyItem.remove(atOffsets: item)
                                })
                            }
                        }
                    }
    
                    class HomeViewModel: ObservableObject {
                        @Published var studySet: StudyModel = StudyModel()
                    }
    
                    struct StudyModel: Hashable{
                        var title: String = ""
                        var days = ["One day", "Two days", "Three days", "Four days", "Five days", "Six days", "Seven days"]
                        var studyGoals = "One day"
                        var studyItem: [StudyItemModel] = [StudyItemModel(itemTitle: "test 1", itemDescription: "hello"), StudyItemModel(itemTitle: "test 2", itemDescription: "hello"), StudyItemModel(itemTitle: "test 3", itemDescription: "hello")]
                    }
    
                    struct StudyItemModel: Hashable, Identifiable{
                        let id = UUID()
                        var itemTitle: String = ""
                        var itemDescription: String = ""
                    }