Why, in the following app when clicking through to ‘Nice Restaurant’ and trying to add a contributor, does the app crash with the error:
Swift/ContiguousArrayBuffer.swift:575: Fatal error: Index out of range
?
The error, in the Xcode debugger, has no obviously useful stack trace and points straight at the ‘@main’ line.
There are no explicit array indices used in the code nor any uses of members like .first
.
I’m using Xcode Version 13.4.1 (13F100)
I’m using simulator: iPhone 13 iOS 15.5 (19F70)
import SwiftUI
struct CheckContribution: Identifiable {
let id: UUID = UUID()
var name: String = ""
}
struct Check: Identifiable {
var id: UUID = UUID()
var title: String
var contributions: [CheckContribution]
}
let exampleCheck = {
return Check(
title: "Nice Restaurant",
contributions: [
CheckContribution(name: "Bob"),
CheckContribution(name: "Alice"),
]
)
}()
struct CheckView: View {
@Binding var check: Check
@State private var selectedContributor: CheckContribution.ID? = nil
func addContributor() {
let newContribution = CheckContribution()
check.contributions.append(newContribution)
selectedContributor = newContribution.id
}
var body: some View {
List {
ForEach($check.contributions) { $contribution in
TextField("Name", text: $contribution.name)
}
Button(action: addContributor) {
Text("Add Contributor")
}
}
}
}
@main
struct CheckSplitterApp: App {
@State private var checks: [Check] = [exampleCheck]
var body: some Scene {
WindowGroup {
NavigationView {
List {
ForEach($checks) { $check in
NavigationLink(destination: {
CheckView(check: $check)
}) {
Text(check.title).font(.headline)
}
}
}
}
}
}
}
I’ve noticed that:
- If I unroll the ForEach($checks) the crash doesn’t occur (but I need to keep the ForEach so I can list all the checks)
- If I don’t take a binding to the CheckContribution (
ForEach($check.contributions) { $contribution in
then the crash doesn’t occur (but I need the binding so subviews can modify the CheckContribution - If I don’t set the selectedContributor then the crash doesn’t occur (but I need the selectedContributor in the real app for navigation purposes)
3
Answers
The cleanest way I could find that actually works is to further separate the nested
ForEach
into a subview and bind thecontributors
array to it.I’m still not sure about the internals of SwiftUI, and why it works this way. I hope it helps. And maybe another more experienced user can provide a clear explanation to this.
If you really want the
Button
to be in theList
, then you could try this approach using a separate view, works well for me:I had the same error but with tabview. Moreover, the fall was only on iOS 15, but on iOS 16 it worked perfectly and there were no falls.
I tried both through indexes, and through checking for finding the index inside the range, but nothing helped.
The solution was found in the process of debugging: I noticed that it was falling even before the predstavlenie appeared (it worked Appear).
I did a simple check to see if the data array is empty
And it worked – there were no more crashes on iOS 15. Apparently there was some problem with the processing of empty arrays before iOS 16.