Another Dude
Another Dude

Reputation: 1452

Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift when removing element from array

I have a form to select some users and assign them a int value.

The model:

class ReadingTime: Identifiable, Hashable {
    var id: Int
    @State var user: User
    @Published var value: Int

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    static func == (lhs: ReadingTime, rhs: ReadingTime) -> Bool {
        lhs.id == rhs.id
    }
    
    init(id: Int, user: User, value: Int) {
        self.id = id
        self._user = State(wrappedValue: user)
        self.value = value
    }
}

The view:

@Binding var times: [ReadingTime]
@State var newUser: User?

func didSelect(_ user: User?) {
    if let user = user {
        readingTime.append(ReadingTime(id: readingTime.nextMaxId,
                                       user: user,
                                       value: 0))
    }
}

// In the body:
VStack(alignment: .leading, spacing: 0) {
    HStack {
        Picker("Select a user", selection: $newUser.onChange(didSelect)) {
                    ForEach(users) {
                        Text($0.name).tag(Optional($0))
                    }
                }
                .id(users)
            }
            VStack(spacing: 8) {
                ForEach(0..<times.count, id: \.self) { i in
                    HStack(spacing: 0) {                            
                        Text(times[i].user.name)
                        TextField("ms", value: $times[i].value, formatter: NumberFormatter())
                        Button(action: {
                            NSApp.keyWindow?.makeFirstResponder(nil)
                            if let index = times.firstIndex(where: { $0 == times[i] }) {
                                times.remove(at: index)
                            }
                            newUser = nil
                        }, label: {
                            Text("REMOVE")
                        })
                    }
                }
            }
        }
    }
}

It looks like this:

enter image description here

However, when deleting an entry in the list, I get this error:

Fatal error: Index out of range: file Swift/ContiguousArrayBuffer.swift

What's going on here?

Upvotes: 9

Views: 19568

Answers (2)

Tom Hakemann
Tom Hakemann

Reputation: 357

Since there is no accepted answer:

Don't use the index of your array to get the specific object inside your ForEach. Like you did before, you're able to access your objects without using count. Using count causes your Index out of range error.

Do it like this:

ForEach(times) { time in
   // ...
   Text(time.user.name)
   // ...
}

Upvotes: 0

vadian
vadian

Reputation: 285140

Modifying the number of items of an array while being enumerated is an evil trap.

0..<times.count creates a temporary static range.

If you remove the first item in the array index 1 becomes 0, index 2 becomes 1 and so on.

Unfortunately there is no index times.count-1 anymore and you get the Index out of range crash when the loop reaches the last index.

You can avoid the crash if you enumerate the array reversed.

Upvotes: 7

Related Questions