Handling the window life cycle with multiple scenes
At a glance
| Item | Summary |
|---|---|
| Purpose | Track scene state across different window types. |
| App architecture | SampleApp composes Window + WindowGroup + volumetric window around ContentView; AppModel owns interaction while RealityView manages the entity graph. |
| Main patterns | SwiftUI scene composition, SwiftUI–RealityKit bridge, Observable state owner |
| Project style | Code-rich sample with 13 scanned Swift file(s) and 687 Swift line(s); resources and generated assets are excluded from those counts. |
Project structure
Source bundle/
├── SampleApp/SampleApp.swift # SceneID, SampleApp
├── SampleApp/AppModel.swift # AppModel, WindowState
├── SampleApp/ScenePhaseMonitor.swift # SceneStateWindowViewModifier, SceneStateWindowGroupViewModifier
├── SampleApp/SphereModel.swift # SphereModel, ColorOption
├── SampleApp/ContentView.swift # ContentView
├── SampleApp/ControlsView.swift # ControlsView
├── SampleApp/SphereVolume.swift # SphereVolume
├── SampleApp/WindowStateButton.swift # WindowStateButton, OpenButton, CloseButton
└── Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json # authored RealityKit content
Structure observations
- The runtime boundary is Window + WindowGroup + volumetric window; the pruned tree lists only files that explain lifecycle, state, or framework integration.
- App code is split into role-named views, models, managers, providers, components, or systems.
- Authored
.realityor Reality Composer Pro content is a real implementation boundary; Swift loads or drives it rather than reproducing its entity graph.
Overall architecture
flowchart LR
SampleApp_1["SampleApp"]
Window___WindowGroup___volumetric_window_2["Window + WindowGroup + volumetric window"]
ContentView_3["ContentView"]
AppModel_4["AppModel"]
RealityView_entity_graph_5["RealityView entity graph"]
RealityKit_entity_graph_6["RealityKit entity graph"]
SampleApp_1 --> Window___WindowGroup___volumetric_window_2
Window___WindowGroup___volumetric_window_2 --> ContentView_3
ContentView_3 --> AppModel_4
AppModel_4 --> RealityView_entity_graph_5
RealityView_entity_graph_5 --> RealityKit_entity_graph_6
Reference code
SampleApp/SampleApp.swift:17 — the app or executable entry declares the outer scene lifecycle.
struct SampleApp: App {
// ...
}The diagram is a responsibility flow, not a claim that every adjacent node directly calls the next. It keeps scene ownership, shared state, RealityKit content, and framework-provider work at separate levels.
Ownership and state
classDiagram
SampleApp *-- AppModel : appModel
WindowState --> WindowState : sphereLauncherWindowState
WindowStateButton --> SphereModel : sphere
OpenButton --> SphereModel : sphere
Ownership evidence
SampleApp/SampleApp.swift:18 — representative stored state or the nearest verified lifecycle anchor.
@State private var appModel = AppModel()| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
SampleApp |
AppModel as appModel |
creates and retains | Only the declaring scope writes |
WindowState |
WindowState as sphereLauncherWindowState |
stores and coordinates | The owning type coordinates writes |
WindowStateButton |
SphereModel as sphere |
stores and coordinates | The owning type coordinates writes |
OpenButton |
SphereModel as sphere |
stores and coordinates | The owning type coordinates writes |
Ownership here is deliberately narrow: @Environment and weak references are shared links, initialized @State or stored services are lifecycle ownership, and a RealityView content closure owns additions to its entity graph without making the SwiftUI view a reference-type owner.
Class and protocol design
| Type | Responsibility | Depends on or conforms to |
|---|---|---|
SampleApp (SampleApp/SampleApp.swift:17) |
Declares app scenes and top-level dependency lifetime. | App |
ContentView (SampleApp/ContentView.swift:10) |
Presents UI and forwards gestures or lifecycle events. | View |
AppModel (SampleApp/AppModel.swift:11) |
Owns observable feature state and domain transitions. | Concrete framework collaborators |
SphereModel (SampleApp/SphereModel.swift:10) |
Owns observable feature state and domain transitions. | Codable, Hashable, Identifiable |
ControlsView (SampleApp/ControlsView.swift:10) |
Presents UI and forwards gestures or lifecycle events. | View |
The source defines no local substitution protocol in the reviewed boundary. Its protocol use is framework-facing (App, View, RealityKit/ARKit protocols, or platform adapters), so this document does not label the whole app protocol-oriented.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
@Environment(AppModel.self) private var appModel (SampleApp/ControlsView.swift:11) |
private |
Use is restricted to the declaration and same-file extensions permitted by Swift. | Inference: Hide implementation details and lifecycle-sensitive state. |
public let realityKitContentBundle = Bundle.module (Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift:11) |
public |
The declaration is available to importing modules, subject to its containing type’s visibility. | Inference: Export the type across the package-module boundary. |
No reviewed declaration uses fileprivate, private(set), open; unmodified Swift declarations are internal.
Reference code
SampleApp/ControlsView.swift:11 — representative visibility boundary.
@Environment(AppModel.self) private var appModel
@Environment(\.openWindow) private var openWindow
@Environment(\.dismissWindow) private var dismissWindow
@SceneStorage("enlarge") private var enlarge: Bool = falseLogic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Scene declaration and dependency lifetime | SampleApp |
The App/entry boundary determines window, volume, and immersive-space lifetime. |
| Presentation, attachments, and gestures | ContentView |
SwiftUI view code forwards user intent and RealityView lifecycle events. |
| Shared feature state and commands | AppModel |
A role-named owner prevents sibling views from duplicating transitions. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| SwiftUI scene composition | SampleApp/SampleApp.swift:17 |
Keeps windows, volumes, and immersive-space lifecycle visible at the app boundary. |
| SwiftUI–RealityKit bridge | SampleApp/SphereVolume.swift:20 |
Builds and updates a RealityKit entity graph from SwiftUI lifecycle closures. |
| Observable state owner | SampleApp/AppModel.swift:11 |
Shares feature state across multiple views without moving framework resources into view values. |
Naming conventions
- Role suffixes are evidence, not decoration: App:
SampleApp; Model:AppModel,SphereModel; View:ContentView,ControlsView. - Protocols: no app-defined protocol in the reviewed source.
- Commands use verb-led methods:
updateWindowState,unregisterWindow,windowState,monitorScenePhase,body. - Files generally match their primary type;
Views,Models,Managers,Providers,Components,Systems, andPackagesfolders describe architectural roles where present.
Architecture takeaways
- Treat
SampleAppas the owner of scene declarations, not as the owner of every RealityKit entity created later. - Keep view-local interaction in SwiftUI, but move provider sessions, playback resources, shared game state, or transport state into a stable owner when their lifetime exceeds one render pass.
Source map
| Source file | Relevant symbols |
|---|---|
SampleApp/SampleApp.swift:10 |
SceneID, SampleApp |
SampleApp/AppModel.swift:11 |
AppModel, WindowState |
SampleApp/ScenePhaseMonitor.swift:57 |
SceneStateWindowViewModifier, SceneStateWindowGroupViewModifier |
SampleApp/SphereModel.swift:10 |
SphereModel, ColorOption |
SampleApp/ContentView.swift:10 |
ContentView |
SampleApp/ControlsView.swift:10 |
ControlsView |
SampleApp/SphereVolume.swift:12 |
SphereVolume |
SampleApp/WindowStateButton.swift:10 |
WindowStateButton, OpenButton, CloseButton |