We have an application with some ‘chat’ functionality where questions are asked and the user can answer with some predefined options: for every question a new view is presented. One of those options is a view with a Picker, since iOS 16 this Picker causes the app to crash when the view with the Picker disappears with following error: Thread 1: Fatal error: Index out of range
positioned at class AppDelegate: UIResponder, UIApplicationDelegate {
. In the log I can see this error: Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range
.
To troubleshoot this issue I refactored the code to a bare minimum where the picker isn’t even used but still cause the error to occur. When I remove the Picker from this view it works again.
View where error occurs
struct PickerQuestion: View {
@EnvironmentObject() var questionVM: QuestionVM
let question: Question
var colors = ["A", "B", "C", "D"]
@State private var selected = "A"
var body: some View {
VStack {
// When removing the Picker from this view the error does not occur anymore
Picker("Please choose a value", selection: $selected) {
ForEach(colors, id: \.self) {
Text($0)
}
}.pickerStyle(.wheel) // with .menu style the crash does not occur
Text("You selected: \(selected)")
Button("Submit", action: {
// In this function I provide an answer that is always valid so I do not
// have to use the Picker it's value
questionVM.answerQuestion(...)
// In this function I submit the answer to the backend.
// The backend will provide a new question which can be again a Picker
// question or another type of question: in both cases the app crashes
// when this view disappears. (the result of the backend is provided to
// the view with `DispatchQueue.main.async {}`)
questionVM.submitAnswerForQuestionWith(questionId: question.id)
})
}
}
}
Parent view where the view above is used (Note: even with all the animation related lines removed the crash still occurs):
struct QuestionContainerView: View {
@EnvironmentObject() var questionVM: QuestionVM
@State var questionVisible = true
@State var questionId = ""
@State var animate: Bool = false
var body: some View {
VStack {
HeaderView(...)
Spacer()
if questionVM.currentQuestion != nil {
ZStack(alignment: .bottom) {
if questionVisible {
getViewForQuestion(question: questionVM.currentQuestion!)
.transition(.asymmetric(
insertion: .move(edge: self.questionVM.scrollDirection == .Next ? .trailing : .leading),
removal: .opacity
))
.zIndex(0)
.onAppear {
self.animate.toggle()
}
.environmentObject(questionVM)
} else {
EmptyView()
}
}
}
}
.onAppear {
self.questionVM.getQuestion()
}
.onReceive(self.questionVM.$currentQuestion) { q in
if let question = q, question.id != self.questionId {
self.questionVisible = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
withAnimation {
self.questionVisible = true
self.questionId = question.id
}
}
}
}
}
func getViewForQuestion(question: Question) -> AnyView {
switch question.questionType {
case .Picker:
return AnyView(TestPickerQuestion(question: question))
case .Other:
...
case ...
}
}
}
The app was made originally for iOS 13 but is still maintained: with every new version of iOS the app kept working as expected until now with iOS 16.