Hello,
I have a simple example using StateObject
and List. When I bind the List(selection:)
to a property of the StateObject
like this:
List(selection: $viewModel.selectedIndex) { ... }
I noticed that each time I push the view using a NavigationLink
, a new instance of the StateObject
is created. However, when I pop the view, the deinit
of the StateObject
is not called.
When is deinit
actually expected to be called in this case?
Example code:
import SwiftUI
@main
struct NavigationViewDeinitSampleApp: App {
var body: some Scene {
WindowGroup {
NavigationStack {
ContentView()
}
}
}
}
struct Item: Hashable {
let text: String
}
@MainActor
fileprivate class ContentViewModel: ObservableObject {
@Published var selectedIndex: Int? = nil
init() {
NSLog("ContentViewModel.init")
}
deinit {
NSLog("ContentViewModel.deinit")
}
}
struct ContentView: View {
@StateObject private var model = ContentViewModel()
let items: [Item] = {
return (0...10).map { i in
Item(text: "\(i)")
}
}()
var body: some View {
List(selection: $model.selectedIndex) {
ForEach(items.indices, id: \.self) { idx in
let item = items[idx]
NavigationLink {
ContentView()
} label: {
Text(item.text)
}
}
}
}
}
Interestingly, if I instead use a plain @State variable inside the View:
@State private var selectedIndex: Int?
...
List(selection: $selectedIndex) { ... }
Then the deinit of the StateObject does get called when the view is popped.
Because there's no sign of deinit being triggered in the first pattern, I’m starting to suspect this might be a SwiftUI bug. Has anyone seen this behavior or have more information about it?
Thanks in advance.
Environment:
- Xcode: 16.4(16F6)
- iOS Simulator: iPhone SE3 iOS16.4(20E247),iPhone SE3 iOS 18.4(22E238)