I have a tab-based SwiftUI app that uses .sheet()
to present a View
that contains email and passwords fields. If I type into those fields then background and foreground the app, the fields are cleared, which makes it impossible to use a password manager. If instead, the view is inlined within the .sheet()
‘s content, the fields are not cleared.
Unexpectedly, removing either the TabView
or NavigationStack
results in the fields not being cleared. It seems that TabView
and NavigationStack
trigger .sheet()
‘s content to be recreated such that the view’s state is lost.
What’s going on here and what’s the right way to achieve the desired result?
Repro steps:
- tap “Form 1”
- type something into the email and password fields
- background the app
- foreground the app
Expect: field values are retained
Actual: field values are reset
Tapping “Form 2”, where the view’s contents are inlined, works as expected.
Demo app:
import SwiftUI
struct ContentView: View {
@State var isShowingForm1 = false
@State var isShowingForm2 = false
@State var email = ""
@State var password = ""
var body: some View {
TabView {
NavigationStack {
List {
Button("Form 1") {
isShowingForm1 = true
}
.sheet(isPresented: $isShowingForm1) {
FormView()
.padding()
}
Button("Form 2") {
isShowingForm2 = true
}
.sheet(isPresented: $isShowingForm2) {
VStack {
TextField("Email", text: $email)
SecureField("Password", text: $password)
}
.padding()
}
}
}
.tabItem {
Label("Tab1", systemImage: "circle")
}
}
}
}
struct FormView: View {
@State var email = ""
@State var password = ""
var body: some View {
VStack {
TextField("Email", text: $email)
SecureField("Password", text: $password)
}
}
}