I would like to report a memory leak issue in watchOS 11.2 that occurs when using .navigationTitle() inside a sheet. This behavior is reproducible both on the simulator and on a real device, but not on iOS. While this does not register as a leak in Instruments, the deinit of the DetailsViewModel is never called, and multiple instances of the view model accumulate in the Memory Graph Debugger. Commenting out .navigationTitle("Sheet View") resolves the issue, and deinit prints as expected. Using @MainActor on the DetailsViewModel does not fix the issue. Nor does switching to @StateObject and using ObservableObject resolve the memory retention.
This issue seems related to other SwiftUI memory leaks that have been reported:
https://developer.apple.com/forums/thread/738840
https://developer.apple.com/forums/thread/736110?login=true&page=1#769898022
https://developer.apple.com/forums/thread/737967?answerId=767599022#767599022
Feedback Number: FB16442048
struct MainView: View {
var body: some View {
NavigationStack {
NavigationLink("Details", value: 1)
.navigationDestination(for: Int.self) { _ in
DetailsView()
}
}
}
}
struct SheetObject: Identifiable {
let id = UUID()
let date: Date
let value: Int
}
@Observable
@MainActor
final class DetailsViewModel {
var sheetObject: SheetObject?
init() {
print("Init")
}
deinit {
print("Deinit")
}
func onAppear() async {
try? await Task.sleep(for: .seconds(2))
sheetObject = .init(date: .now, value: 1)
}
}
struct DetailsView: View {
@State private var viewModel = DetailsViewModel()
@Environment(\.dismiss) var dismiss
var body: some View {
Text("Detail View. Going to sheet, please wait...")
.task {
await viewModel.onAppear()
}
.sheet(item: $viewModel.sheetObject) { sheetObject in
SheetView(sheetObject: sheetObject)
.onDisappear {
dismiss()
}
}
}
}
struct SheetView: View {
let sheetObject: SheetObject
@Environment(\.dismiss) var dismiss
var body: some View {
NavigationStack {
VStack {
Text(sheetObject.date.formatted())
Text(sheetObject.value.formatted())
Button("Dismiss") {
dismiss()
}
}
.navigationTitle("Sheet View") // This line causes a memory leak. Commenting out, you will see "Deinit" be printed.
}
}
}