I am designing a SwiftUI view, which is meant to be presented modally (using a sheet modifier). I want the view to have two buttons inside its own navigation bar and this somehow forced me to use NavigationView inside the body of the view.
The view is going to be a simple form and one of its components is a TextField, which I want to automatically activate (set focus on), when the view appears. For that, I used the @FocusState
property wrapper.
In spite of using almost the same view's body structure as in another file before, this new form kept crashing whenever my preview updated, yielding a long report.
Here are some essentials of that report:
Code Type: X86-64 (Native)
Role: Non UI
Parent Process: launchd_sim [30182]
Responsible Process: SimulatorTrampoline [872]
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: SIGNAL 4 Illegal instruction: 4
Terminating Process: exc handler [30404]
Thread 0 Crashed:: Dispatch queue: BSXPCCnx:com.apple.dt.xcode-previews.systemservices (BSCnx:client:com.apple.dt.uv.agent-preview-nonui-service)
0 SwiftUI 0x7fff5c970f89 static FocusState._makeProperty<A>(in:container:fieldOffset:inputs:) + 160
1 SwiftUI 0x7fff5c97141e protocol witness for static DynamicProperty._makeProperty<A>(in:container:fieldOffset:inputs:) in conformance FocusState<A> + 25
I immediately saw that there was a problem with @FocusState
. Indeed, removing it stopped crashing the app. But how was the other view I mentioned working completely fine? I started comparing them.
The old view's body started with a Form and that view was embedded in a NavigationView in its preview. In the new view, the NavigationView was moved into its body itself. That was the key!
I prepared a little example view to illustrate this issue. This version crashes when you try to launch the preview:
import SwiftUI
struct Test: View {
@FocusState var focusedFieldNumber: Int?
@State private var comment = ""
var body: some View {
NavigationView {
Form {
TextEditor(text: $comment)
.focused($focusedFieldNumber, equals: 1)
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.75) {
self.focusedFieldNumber = 1
}
}
}
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}
And this version (extract) solves the problem:
import SwiftUI
struct Test: View {
// ...
var body: some View {
// Removed NavigationView.
Form {
// ...
}
// ...
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
// Added NavigationView.
NavigationView {
Test()
}
}
}
Why does the position of NavigationView decide whether the preview crashes or not? Where does this behavior come from? I would love to hear something from Apple Developers. Thank you!