I have an array of elements that contains a nested array of other elements inside.
When deleting a row of an array, sometimes a crash occurs with the message ‘Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range’, not pointing at concrete line of code.
Here’s my minimal reproducible code:
// View components
struct ContentView: View {
@StateObject var viewModel: ViewModel = .init()
var body: some View {
ScrollView {
LazyVStack {
ForEach($viewModel.assetsRows, id: .self) { assetsRow in
VStack {
Button(action: {
viewModel.deleteSelected(assetsIn: assetsRow.wrappedValue)
}, label: {
HStack {
Image(systemName: "trash")
Text("Delete row")
}
})
RowView(assetsRow: assetsRow)
}
}
}
}
}
}
struct RowView: View {
@Binding var assetsRow: AssetsRowModel
var body: some View {
ScrollView(.horizontal) {
LazyHStack {
ForEach($assetsRow.items, id: .self) { item in
GridItemView(
assetItem: item,
image: .init(systemName: "photo.fill")
)
}
}
}
}
}
struct GridItemView: View {
@Binding var assetItem: AssetItem
@State var image: Image?
var body: some View {
Group {
if let image = image {
image
} else {
ProgressView()
}
}
.frame(width: 200, height: 120)
.overlay(alignment: .bottomTrailing) {
Toggle(isOn: $assetItem.isSelected) {
Text("checkmark")
}
.padding(4)
}
.onAppear {
// fetch image logic
}
}
}
@MainActor final class ViewModel: ObservableObject {
@Published var assetsRows: [AssetsRowModel] = {
var array: [AssetsRowModel] = []
for i in 0..<30 {
array.append(.init(items: [.init(), .init(), .init()]))
}
return array
}()
// removing items causes crash (not 100% times)
func deleteSelected(assetsIn row: AssetsRowModel) {
withAnimation {
assetsRows.removeAll { element in
element.id == row.id
}
}
}
// other fetching logic
}
// Models
struct AssetsRowModel: Identifiable, Equatable, Hashable {
var id = UUID()
var items: [AssetItem]
}
struct AssetItem: Identifiable, Hashable {
var id = UUID()
var isSelected = false
}
extension AssetItem: Equatable {
static func ==(lhs: AssetItem, rhs: AssetItem) -> Bool {
(lhs.id == rhs.id)
}
}
Tried to change @Binding
to @State
in RowView
, it’s prevent the crash, but isSelected
doesn’t working properly, because it’s not ‘binding’ with viewModel’s value.
I guess this is an internal SwiftUI bug. (Xcode 15.4, iOS 17+)