I present a view as a sheet that contains a TextField and a viewModel. The sheet uses .autocorrectionDisabled() on the TextField.
If I open the sheet and dismiss it without editing, the viewModel is released as expected. But if I edit the TextField and then dismiss the sheet, the viewModel is not deinitialized (it remains retained).
Test Outputs:
// # Present 1st time - Correct ✅
TestViewModel - init - 0x0000600000c12ee0
// Dismiss wihtout editing, deinitialized correctly
TestViewModel - deinit - 0x0000600000c12ee0
// # Present 2st time - Incorrect ❌
TestViewModel - init - 0x0000600000c006c0
// Edit and dismiss, no deinit here
// # Present 3rd time - Incorrect ❌
TestViewModel - init - 0x0000600000c113b0
// Enter text field to edit will deinit previous one
TestViewModel - deinit - 0x0000600000c006c0
// Dismiss, no deinit
// TestViewModel (0x0000600000c113b0) remains retained ⚠️
Using .autocorrectionDisabled(true) gives the same problem. Removing or commenting out .autocorrectionDisabled() fixes the issue, but I don't want autocorrection enabled.
Is this a known SwiftUI bug? Has anyone encountered it and found a workaround?
Sample code:
@main
struct TestApp: App {
@State private var isPresentingSheet: Bool = false
var body: some Scene {
WindowGroup {
VStack {
Button("Present Sheet") {
self.isPresentingSheet = true
}
}
.sheet(isPresented: $isPresentingSheet) {
TestTextInputView()
}
}
}
}
View (check the HERE line):
struct TestTextInputView: View {
@StateObject private var viewModel = TestTextInputViewModel()
@FocusState private var isFocused: Bool
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
Form {
TextField("Name", text: $viewModel.name)
.autocorrectionDisabled() // HERE: Comment it out will work without any issue.
.focused($isFocused)
.submitLabel(.done)
.onSubmit {
self.isFocused = false
}
}
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button("Cancel") { dismiss() }
}
}
}
}
}
View Model:
final class TestTextInputViewModel: ObservableObject {
@Published var name: String = ""
deinit {
print("TestViewModel - deinit - \(Unmanaged.passUnretained(self).toOpaque())")
}
init() {
print("TestViewModel - init - \(Unmanaged.passUnretained(self).toOpaque())")
}
}