Post

Replies

Boosts

Views

Activity

Reply to visionOS 26: is there any way yet to distinguish a user-initiated window close from system out-of-FoV backgrounding
Michael, Filed: FB24152895 — the enhancement request for scene presentation on app re-activation (the defaultLaunchBehavior gap discussed above). On the immersion-style restyle: On the immersion-style restyle: in the spirit of not wasting your time, I built a minimal repro project first, and in isolation, the .mixed → .full flip 300 ms after opening transitions correctly. So the black-render we hit required more than the restyle timing alone; the missing ingredient is presumably work our renderer performs during the transition (IBL environment application, a large textured sky dome). Since ensuring the selection is correct at presentation fixed our app outright, I can't currently produce a failing repro worth your time. If it ever resurfaces, I'll capture it and file then. On avoiding undocumented behavior — point taken, and let me be precise about the two things in play. The out-of-view backgrounding timing: nothing in the app depends on it. The in-space affordance is deliberately benign precisely so the timing can be anything, so there's no fragility there by design. The activation-observer recovery is the other piece: as far as we can tell it uses only documented API (didBecomeActiveNotification plus openWindow(id:)), but if what you're flagging is a reliance on an activation sequence that isn't contractually guaranteed, I'd rather know than assume. That's the one piece I'd most welcome a supported alternative for, if one exists. The offer you made to recommend alternative approaches is genuinely appreciated, and this is where it would help most. Thanks again, Rich
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Aug ’26
Reply to visionOS 26: is there any way yet to distinguish a user-initiated window close from system out-of-FoV backgrounding
Michael, thanks much for your help which led to resolving our issues. We ran Hello World on-device, studied both samples, and rebuilt our app's window lifecycle around them. Everything you suggested is now implemented: Adopted: A window-first startup screen (your last question answered by implementation): the app now launches into a front-door window — Create / Open / content library — and the immersive space opens only on user intent. Structurally it's Hello World's single-window pattern: the front door, the authoring menu, and the player's transport bar are three content states of ONE persistent Window scene, not separate windows. (We tried a dedicated startup window with hand-offs first; the single-window design proved far cleaner.) On your side-by-side question: verified on-device — at the front door (no space open), other apps' windows coexist normally; entering a scene hides them (both our modes are immersive spaces — even Passthrough is .mixed); exiting back to the front door brings them back. During editing itself, full immersion IS the product — a spatial 3D editor — so that stays immersive-first by design. The Objects-in-Orbit attachment pattern for the "main window closed while immersed" state: a persistent ViewAttachmentComponent with BillboardComponent and conditional content, offering Reopen plus an Exit that returns to the startup window. One practical note: we place it at 0.85 m — at 1.0–1.3 m it can sit behind the user's own tool windows, and depth compositing is honest. exit(0) is gone. Message received on programmatic quit being unsupported. "Quitting" is now: exit to the startup window → user closes it → zero scenes → the system recedes the app. (Your Solar System module's Exit button was a helpful precedent.) On your remaining question — the file-launch difference: scenes can be authored with a play-on-open flag, so another Vision Pro user tapping a shared scene file gets a playback experience — watching the created animation via a compact transport bar, the Solar System pattern — rather than the authoring UI. Deliberate authorial intent, not an inconsistency. Findings from the implementation your team may find useful: defaultLaunchBehavior(.presented) doesn't cover RE-ACTIVATION of a running app: with the main window closed and tool windows alive, an icon tap re-presents only the tool windows, .presented notwithstanding. We recover via a UIApplication.didBecomeActiveNotification observer that reopens the main window — works, but feels like it should be the launch-behavior API's job. The last-closed-window zombie shaped most of our bugs: a window closed as the last regular scene is backgrounded, never eliminated — no onDisappear, and it still counts as "open." Practical corollary we learned the hard way: programmatic dismissWindow teardowns must be ordered so every dismissal has an open sibling, or scenes zombify and the app can't recede. Your lifecycle sample's three-state enum (.inTransition / .open(phase) / .closed) turned out to be essential — we think it might deserve louder billing in the docs. An immersion-style transition asymmetry worth a look: the .immersionStyle(selection:) value is read at presentation, and a .mixed → .full restyle applied to a just-opened space fails reliably on-device (space renders pure black; .full → .mixed tolerates the same timing). We fixed it by ensuring the selection binding is correct before openImmersiveSpace (computed binding reading our model live). Happy to file a Feedback with a repro if useful. An open question on the earlier scenePhase discussion: the ~61 s out-of-view backgrounding seems CONDITIONAL, and we've been unable to pin the conditions experimentally. Our parked main window reliably backgrounds after ~a minute in BOTH immersion styles, with or without other windows open; your Orbit module's lone parked window stayed .active for 3+ minutes. Ruled out experimentally: immersion style (both behave the same), presence of other windows (none open, still backgrounds), and the window's own update activity (we made our menu fully static — still backgrounds on schedule). Whatever the discriminating condition is, it isn't observable from the app side. If the rules were documented, apps relying on the show-benign-UI-on-background pattern could set expectations correctly. Thanks again for the direction — the samples were exactly the map we needed. FB24076974 remains our enhancement request for a close signal on the last nonimmersive scene; everything else above is running today.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Aug ’26
Reply to visionOS 26: is there any way yet to distinguish a user-initiated window close from system out-of-FoV backgrounding
Thanks Michael — enhancement request filed as FB24076974, and a focused test project is here: https://www.icloud.com/iclouddrive/0fcptDNbtKk0CCcs-EYMS_2bA#WindowCloseRepro Building it produced results worth reporting, including a correction to my original post. Complete flow: Launch opens the primary window (the editor menu) which immediately opens a fully immersive space — the space IS the product: users build and animate 3D scenes inside it in .full immersion. They open and close secondary tool windows (materials, timeline, lighting, …) around the space as they work, and park any window — including the primary — beside or behind them. There is also a player flow: tapping a scene file in Files launches straight into the immersive space with a single compact transport-bar window. Correction first: in the focused repro (visionOS 26.5, on-device), the session-identifier mismatch I reported does NOT reproduce. While another regular window is open, closing a window delivers didDisconnect + didDiscardSceneSessions within ~30 ms, carrying an identifier that matches the closed window's view-reachable windowScene.session.persistentIdentifier, plus root-view onDisappear — attribution works, exactly as your window-life-cycle article describes. My earlier in-app observations of unmatched identifiers were evidently stale-session discards misattributed to the close. I'm happy to be wrong about that half. The gap that DOES reproduce, cleanly: closing the LAST regular window while the fully immersive space remains open. On-device, that close produces didEnterBackground + scenePhase → background and then nothing — no disconnect, no discard, no onDisappear, session still in openSessions — for the entire time the user remains in the immersive space. That signature is identical to the ~61 s out-of-view backgrounding, so the app cannot distinguish "the user closed my last window and is now standing inside my environment with no chrome at all" from "my window is parked behind the user". Your doc states this case directly ("The last closed nonimmersive scene enters the ScenePhase.background phase but doesn't immediately receive the onDisappear() callback") — for a fully-immersive app it is the one lifecycle moment we most need to observe (dismiss the space, save state, end the session), and the one close that produces no signal. On your concern — the app never dismisses its own last scene. The problem is the reverse direction: after the USER closes the last window, they're still inside our fully immersive environment, which has no close affordance of its own. From the user's perspective that close should end the session; the app just has nothing to act on. The enhancement filed: deliver onDisappear/disconnect (or a dismissal callback with a reason) to the last nonimmersive scene when an immersive space remains open — or extend the association model from your "Associating a window with an immersive space" sample (which pairs a pushed window with a space) to a primary window. One question meanwhile: is there a recommended pattern today for a fully-immersive app whose last window closes — is "Embedding controls in an immersive space" (persistent in-space chrome) the intended fallback?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jul ’26
Reply to visionOS 26: is there any way yet to distinguish a user-initiated window close from system out-of-FoV backgrounding
Michael, Filed: FB24152895 — the enhancement request for scene presentation on app re-activation (the defaultLaunchBehavior gap discussed above). On the immersion-style restyle: On the immersion-style restyle: in the spirit of not wasting your time, I built a minimal repro project first, and in isolation, the .mixed → .full flip 300 ms after opening transitions correctly. So the black-render we hit required more than the restyle timing alone; the missing ingredient is presumably work our renderer performs during the transition (IBL environment application, a large textured sky dome). Since ensuring the selection is correct at presentation fixed our app outright, I can't currently produce a failing repro worth your time. If it ever resurfaces, I'll capture it and file then. On avoiding undocumented behavior — point taken, and let me be precise about the two things in play. The out-of-view backgrounding timing: nothing in the app depends on it. The in-space affordance is deliberately benign precisely so the timing can be anything, so there's no fragility there by design. The activation-observer recovery is the other piece: as far as we can tell it uses only documented API (didBecomeActiveNotification plus openWindow(id:)), but if what you're flagging is a reliance on an activation sequence that isn't contractually guaranteed, I'd rather know than assume. That's the one piece I'd most welcome a supported alternative for, if one exists. The offer you made to recommend alternative approaches is genuinely appreciated, and this is where it would help most. Thanks again, Rich
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Aug ’26
Reply to visionOS 26: is there any way yet to distinguish a user-initiated window close from system out-of-FoV backgrounding
Michael, thanks much for your help which led to resolving our issues. We ran Hello World on-device, studied both samples, and rebuilt our app's window lifecycle around them. Everything you suggested is now implemented: Adopted: A window-first startup screen (your last question answered by implementation): the app now launches into a front-door window — Create / Open / content library — and the immersive space opens only on user intent. Structurally it's Hello World's single-window pattern: the front door, the authoring menu, and the player's transport bar are three content states of ONE persistent Window scene, not separate windows. (We tried a dedicated startup window with hand-offs first; the single-window design proved far cleaner.) On your side-by-side question: verified on-device — at the front door (no space open), other apps' windows coexist normally; entering a scene hides them (both our modes are immersive spaces — even Passthrough is .mixed); exiting back to the front door brings them back. During editing itself, full immersion IS the product — a spatial 3D editor — so that stays immersive-first by design. The Objects-in-Orbit attachment pattern for the "main window closed while immersed" state: a persistent ViewAttachmentComponent with BillboardComponent and conditional content, offering Reopen plus an Exit that returns to the startup window. One practical note: we place it at 0.85 m — at 1.0–1.3 m it can sit behind the user's own tool windows, and depth compositing is honest. exit(0) is gone. Message received on programmatic quit being unsupported. "Quitting" is now: exit to the startup window → user closes it → zero scenes → the system recedes the app. (Your Solar System module's Exit button was a helpful precedent.) On your remaining question — the file-launch difference: scenes can be authored with a play-on-open flag, so another Vision Pro user tapping a shared scene file gets a playback experience — watching the created animation via a compact transport bar, the Solar System pattern — rather than the authoring UI. Deliberate authorial intent, not an inconsistency. Findings from the implementation your team may find useful: defaultLaunchBehavior(.presented) doesn't cover RE-ACTIVATION of a running app: with the main window closed and tool windows alive, an icon tap re-presents only the tool windows, .presented notwithstanding. We recover via a UIApplication.didBecomeActiveNotification observer that reopens the main window — works, but feels like it should be the launch-behavior API's job. The last-closed-window zombie shaped most of our bugs: a window closed as the last regular scene is backgrounded, never eliminated — no onDisappear, and it still counts as "open." Practical corollary we learned the hard way: programmatic dismissWindow teardowns must be ordered so every dismissal has an open sibling, or scenes zombify and the app can't recede. Your lifecycle sample's three-state enum (.inTransition / .open(phase) / .closed) turned out to be essential — we think it might deserve louder billing in the docs. An immersion-style transition asymmetry worth a look: the .immersionStyle(selection:) value is read at presentation, and a .mixed → .full restyle applied to a just-opened space fails reliably on-device (space renders pure black; .full → .mixed tolerates the same timing). We fixed it by ensuring the selection binding is correct before openImmersiveSpace (computed binding reading our model live). Happy to file a Feedback with a repro if useful. An open question on the earlier scenePhase discussion: the ~61 s out-of-view backgrounding seems CONDITIONAL, and we've been unable to pin the conditions experimentally. Our parked main window reliably backgrounds after ~a minute in BOTH immersion styles, with or without other windows open; your Orbit module's lone parked window stayed .active for 3+ minutes. Ruled out experimentally: immersion style (both behave the same), presence of other windows (none open, still backgrounds), and the window's own update activity (we made our menu fully static — still backgrounds on schedule). Whatever the discriminating condition is, it isn't observable from the app side. If the rules were documented, apps relying on the show-benign-UI-on-background pattern could set expectations correctly. Thanks again for the direction — the samples were exactly the map we needed. FB24076974 remains our enhancement request for a close signal on the last nonimmersive scene; everything else above is running today.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Aug ’26
Reply to visionOS 26: is there any way yet to distinguish a user-initiated window close from system out-of-FoV backgrounding
Thanks Michael — enhancement request filed as FB24076974, and a focused test project is here: https://www.icloud.com/iclouddrive/0fcptDNbtKk0CCcs-EYMS_2bA#WindowCloseRepro Building it produced results worth reporting, including a correction to my original post. Complete flow: Launch opens the primary window (the editor menu) which immediately opens a fully immersive space — the space IS the product: users build and animate 3D scenes inside it in .full immersion. They open and close secondary tool windows (materials, timeline, lighting, …) around the space as they work, and park any window — including the primary — beside or behind them. There is also a player flow: tapping a scene file in Files launches straight into the immersive space with a single compact transport-bar window. Correction first: in the focused repro (visionOS 26.5, on-device), the session-identifier mismatch I reported does NOT reproduce. While another regular window is open, closing a window delivers didDisconnect + didDiscardSceneSessions within ~30 ms, carrying an identifier that matches the closed window's view-reachable windowScene.session.persistentIdentifier, plus root-view onDisappear — attribution works, exactly as your window-life-cycle article describes. My earlier in-app observations of unmatched identifiers were evidently stale-session discards misattributed to the close. I'm happy to be wrong about that half. The gap that DOES reproduce, cleanly: closing the LAST regular window while the fully immersive space remains open. On-device, that close produces didEnterBackground + scenePhase → background and then nothing — no disconnect, no discard, no onDisappear, session still in openSessions — for the entire time the user remains in the immersive space. That signature is identical to the ~61 s out-of-view backgrounding, so the app cannot distinguish "the user closed my last window and is now standing inside my environment with no chrome at all" from "my window is parked behind the user". Your doc states this case directly ("The last closed nonimmersive scene enters the ScenePhase.background phase but doesn't immediately receive the onDisappear() callback") — for a fully-immersive app it is the one lifecycle moment we most need to observe (dismiss the space, save state, end the session), and the one close that produces no signal. On your concern — the app never dismisses its own last scene. The problem is the reverse direction: after the USER closes the last window, they're still inside our fully immersive environment, which has no close affordance of its own. From the user's perspective that close should end the session; the app just has nothing to act on. The enhancement filed: deliver onDisappear/disconnect (or a dismissal callback with a reason) to the last nonimmersive scene when an immersive space remains open — or extend the association model from your "Associating a window with an immersive space" sample (which pairs a pushed window with a space) to a primary window. One question meanwhile: is there a recommended pattern today for a fully-immersive app whose last window closes — is "Embedding controls in an immersive space" (persistent in-space chrome) the intended fallback?
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jul ’26