When using keyboardShortcut, instance of StateObject is not deinitialized

When adding a keyboardShortcut to a Button that references the @StateObject model in Button.action as shown below, model.deinit is not called even if this view has disappeared. On redisplaying, both model.deinit and model.init are called.

Without the keyboardShortcut, model.deinit is called when the screen has disappeared. Also, when I explicitly specified [weak model = self.model], the deinit was called.

Is it a bug that we need to use weak when using keyboardShortcut?

Test environment

  • Xcode Version 15.0 (15A240d)
  • iOS 17.0 Simulator iOS 16.4 Simulator, (iPhone SE 3rd generation)
import SwiftUI

@main
struct StateObjectDeinitSampleApp: App {
    var body: some Scene {
        WindowGroup {
            NavigationStack {
                MainView()
            }
        }
    }
}

struct MainView: View {
    var body: some View {
        NavigationLink(value: true) {
            Text("GO")
        }
        .navigationDestination(for: Bool.self) { value in
            if value {
                ContentView()
            }
        }
    }
}

struct ContentView: View {
    @StateObject var model = Model()
    
    var body: some View {
        VStack {
            Button("Toggle") {
                model.flag.toggle()
            }
            .keyboardShortcut(.space, modifiers: [])
        }
        .padding()
    }
}

@MainActor
class Model: ObservableObject{
    @Published var flag: Bool = false
    
    init() {
        print("Model.init")
    }
    
    deinit {
        print("Model.deinit")
    }
}

//  weak.
struct ContentView: View {
    @StateObject var model = Model()
    
    var body: some View {
        VStack {
            Button("Toggle") { [weak model = self.model] in
                model?.flag.toggle()
            }
            .keyboardShortcut(.space, modifiers: [])
        }
        .padding()
    }
}

Upon further investigation, I believe there's a high possibility that this is a bug.

In the code below, model2 is not referenced by Button.action, yet deinit is not called for model2 just like for model.

struct ContentView: View {
    @StateObject var model = Model()
    @StateObject var model2 = Model2()
    
    var body: some View {
        VStack {
            Button("Toggle") {
                model.flag.toggle()
            }
            .keyboardShortcut(.space, modifiers: [])
        }
        .padding()
    }
}

@MainActor
class Model: ObservableObject{
    @Published var flag: Bool = false
    
    init() {
        print("Model.init")
    }
    
    deinit {
        print("Model.deinit")
    }
}

@MainActor
class Model2: ObservableObject{
    @Published var flag: Bool = false
    
    init() {
        print("Model2.init")
    }
    
    deinit {
        print("Model2.deinit")
    }
}

When using keyboardShortcut, instance of StateObject is not deinitialized
 
 
Q