JSK
JSK

Reputation: 197

Removing from array - Fatal error: Index out of range - SwiftUI Binding

I am trying to get my head around SwiftUI bindings. Here I display an array in a view and bind the values to the second view. In the second view I remove the data from the array.

However I get the following,

Fatal error: Index out of range

I am not getting an error for self.person.notes.remove(at: self.index) in fact this is actually removing the note in the array. It must be in the first view when using ForEach as the array has been modified and now it is out of bounds. But I am unsure how to get around this ? Surely the Binding should have resolved this.

View 1

ForEach(self.person.notes.indices, id:\.self) { index in

   NoteView(person: self.$person, room: self.$home.notes[index], index: index)

}

View 2

@Binding var person: Person
@Binding var note: Note
var index: Int

if self.index > 0 {
       Button(action: {
             self.person.notes.remove(at: self.index)
       }) {
           Text("Remove")
       }
 }

Any idea how this is supposed to work in SwiftUI ?

Upvotes: 6

Views: 1966

Answers (1)

Simon
Simon

Reputation: 1860

Removing with a pre given index is always a risk. Possibility of data changing in background etc. Get the index at the time you try to remove the item to be sure to have the right one. In case the data changed somewhere else. This should help you understand:

struct ContentView: View {
    
    @State var myData: Array<String> = ["first", "second", "third"]
    
    var body: some View {
        ForEach(self.myData, id: \.self) { data in
            SecondView(myData: self.$myData, data: data)
        }
    }
}

struct SecondView: View {
    
    @Binding var myData: Array<String>
    // the data that is being tied to the view
    var data: String
    
    var body: some View {
        
        Button(action: {
            // calculate the index at the time of removal
            if let index = self.myData.firstIndex(of: self.data) {
                self.myData.remove(at: index)
            }
        }) {
            Rectangle().foregroundColor(Color.red).frame(width: 100, height: 100).overlay(Text("Button \(self.data)"))
        }
        
    }
}

Produces this:

enter image description here

As you mentioned your error lies in the first view. This is most likely because you are building the ForEach with the number of indices. The moment you remove one, the ForEach runs into the error as it loses "track" of the item and the item with the index the ForEach provided does not exist anymore. Try this:

ForEach(self.person.notes, id:\.self) { note in

   // Remove the index and calculate it on the second view as shown above. This then should solve the problem
   NoteView(person: self.$person, room: self.$home.notes)

}

Upvotes: 2

Related Questions