Not receive onDisappear event on the first WindowGroup

Hi, I'm working on visionOS and find I can't get onDisappear event just on the first window after app launch. It comes like that:


WindowGroup(id:"WindowA"){
    MyView()
    .onDisappear(){
       print("WindowA disappear")
    }
}

WindowGroup(id:"WindowB"){
    MyView()
    .onDisappear(){
       print("WindowB disappear")
    }
}

WindowGroup(id:"WindowC"){
    MyView()
    .onDisappear(){
       print("WindowC disappear")
    }
}
  • When the app first launch, it will open WindowA automatically
  • And then I open WindowB and WindowC programatically.
  • Then I tap the close button on window bar below window.
    • If I close WindowB/WindowC, I can receive onDisappear event
    • If I close WindowA, I can't receive onDisappear event
  • If I reopen WindowA after it is closed and then close it again by tap the close button below window, I can receive onDisappear event

Is there any logic difference for the first window on app launch? How can I get onDisappear Event for it.

I'm using Xcode 16 beta 2

Answered by Vision Pro Engineer in 795851022

@wangdinglu , try also putting scenePhase in the view you want to check the phase of.

scenePhase gives you different values depending on where you read it from. The app level is an aggregate of the phases of the windows of your app.

If you put it in the view, it reports the phase of the view.

For instance, if you have a ContentView and an ImmersiveSpace, and with the ImmersiveSpace open you want to see when ContentView is closed (and it's the only window open), scenePhase in ContentView would tell you when that window was closed.

Another way to test this issue is just use the Official "Hello World" example. I just add onDisappear to the first window.

   WindowGroup("Hello World", id: "modules") {
            Modules()
                .environment(model)
                .onDisappear(){
                    print("Hello World Window On Disappear")
                }
        }
        .windowStyle(.plain)

Run the demo and open either immersive space, then tap the close X button below the Hello World window. And no onDisappear event called.

Hi @wangdinglu ,

This is interesting behavior. What's happening here is that you're not seeing the onDisappear of the last window to be closed. I'll attach some code here and you can play around with it. It's not the first window to be launched by the app that doesn't receive the closure, it's the last window to be closed by the user.

Try opening all 3 and then closing b and then a and then c. You'll notice that "a" does print a disappearing message since it's not the last window to be closed, but "c" won't.

I believe this is happening due to then app becoming inactive and then backgrounded when the last window is closed, so it doesn't print to the console when you'd expect it to. You can monitor this with scenePhase as I show in my code. You can monitor when the entire app becomes backgrounded, which you'd then know that you closed the final window.

import SwiftUI

@main
struct OnDisappearTestApp: App {
    @Environment(\.openWindow) private var openWindow
    @Environment(\.scenePhase) private var scenePhase
    
    var body: some Scene {
        WindowGroup(id: "a") {
            VStack {
                Text("a")
                Button("open b"){
                    openWindow(id: "b")
                }
                Button("open c"){
                    openWindow(id: "c")
                }
            }
            .onDisappear {
                print("a disappear")
            }
            .onAppear {
                print("a appear")
            }
        }
        
        
        WindowGroup(id: "b") {
            Text("b")
                .onAppear {
                    print("b appear")
                }
                .onDisappear {
                    print("b disappear")
                }
        }
        
        WindowGroup(id: "c") {
            Text("c")
                .onAppear {
                    print("c appear")
                }
                .onDisappear {
                    print("c disappear")
                }
        }
        
        .onChange(of: scenePhase) { _, phase in
            switch phase {
            case .active:
                print("active")
            case .inactive:
                print("inactive")
            case .background:
                print("background -> a/b/c disappeared")
            @unknown default:
                print("unknown default")
            }
        }
    }
}
Accepted Answer

@wangdinglu , try also putting scenePhase in the view you want to check the phase of.

scenePhase gives you different values depending on where you read it from. The app level is an aggregate of the phases of the windows of your app.

If you put it in the view, it reports the phase of the view.

For instance, if you have a ContentView and an ImmersiveSpace, and with the ImmersiveSpace open you want to see when ContentView is closed (and it's the only window open), scenePhase in ContentView would tell you when that window was closed.

Not receive onDisappear event on the first WindowGroup
 
 
Q