Sample CodevisionOSReviewed 2026-07-21View on Apple Developer

Playing immersive media with RealityKit

At a glance

Item Summary
Purpose Create an immersive video playback experience with RealityKit.
App architecture RealityKitImmersivePlaybackApp composes WindowGroup + ImmersiveSpace; AppModel keeps player and presentation state stable while ProgressivePlayerImmersiveSpace maps it into RealityKit immersive playback.
Main patterns SwiftUI scene composition, SwiftUI–RealityKit bridge, Explicit immersive-space lifecycle, Observable state owner, Async update consumer
Project style Code-rich sample with 19 scanned Swift file(s) and 1433 Swift line(s); resources and generated assets are excluded from those counts.

Project structure

Source bundle/
└── RealityKitImmersivePlayback/
    ├── RealityKitImmersivePlaybackApp.swift  # RealityKitImmersivePlaybackApp
    ├── Models/
    │   ├── PlayerModel.swift  # PlayerModel
    │   ├── AppModel.swift  # AppModel, ImmersiveSpaceState, WindowState
    │   └── VideoModel.swift  # VideoModel, ContentType, Mode
    ├── Scenes/
    │   └── PlayerImmersiveSpace.swift  # PlayerImmersiveSpace, ProgressivePlayerImmersiveSpace, SpatialPlayerImmersiveSpace
    └── Views/
        ├── ContentView.swift  # ContentView
        ├── ImmersiveControlsView.swift  # ImmersiveControlsView
        └── LoadingIndicator.swift  # LoadingIndicator, LoadingIndicatorOverlay, LoadingIndicatorSpatialOverlay

Structure observations

  • The runtime boundary is WindowGroup + ImmersiveSpace; 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.

Overall architecture

Reference code

RealityKitImmersivePlayback/RealityKitImmersivePlaybackApp.swift:11 — the app or executable entry declares the outer scene lifecycle.

struct RealityKitImmersivePlaybackApp: 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

Ownership evidence

RealityKitImmersivePlayback/RealityKitImmersivePlaybackApp.swift:17 — representative stored state or the nearest verified lifecycle anchor.

@main
struct RealityKitImmersivePlaybackApp: App {
    // ...
    @State private var appModel = AppModel()
    // ...
}
Owner Object or state Relationship Mutation authority
RealityKitImmersivePlaybackApp AppModel as appModel creates and retains Only the declaring scope writes
AppModel VideoModel as selectedVideo stores and coordinates Only the declaring scope writes
AppModel PlayerModel as playerModel stores and coordinates The owning type coordinates writes
AppModel WindowState as windowState 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
RealityKitImmersivePlaybackApp (RealityKitImmersivePlayback/RealityKitImmersivePlaybackApp.swift:11) Declares app scenes and top-level dependency lifetime. App
ProgressivePlayerImmersiveSpace (RealityKitImmersivePlayback/Scenes/PlayerImmersiveSpace.swift:30) Provides a sample-specific value or framework adapter. Scene
AppModel (RealityKitImmersivePlayback/Models/AppModel.swift:15) Owns observable feature state and domain transitions. Concrete framework collaborators
PlayerModel (RealityKitImmersivePlayback/Models/PlayerModel.swift:13) Owns observable feature state and domain transitions. Concrete framework collaborators
VideoModel (RealityKitImmersivePlayback/Models/VideoModel.swift:11) Owns observable feature state and domain transitions. Hashable, Identifiable
ContentView (RealityKitImmersivePlayback/Views/ContentView.swift:10) Presents UI and forwards gestures or lifecycle events. View
ImmersiveControlsView (RealityKitImmersivePlayback/Views/ImmersiveControlsView.swift:11) 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
private(set) var selectedVideo: VideoModel? (RealityKitImmersivePlayback/Models/AppModel.swift:23) private(set) The getter keeps its wider visibility, while mutation stays inside the declaring type and its permitted same-file extensions. Inference: Protect a state invariant while allowing observation.
private let isPlayerItemReadyToPlay: Bool (RealityKitImmersivePlayback/Models/PlaybackReadiness.swift:11) private Use is restricted to the declaration and same-file extensions permitted by Swift. Inference: Hide implementation details and lifecycle-sensitive state.
fileprivate var preferredPlaybackScene: AppModel.PlaybackScene { (RealityKitImmersivePlayback/Models/VideoModel.swift:84) fileprivate Use is restricted to declarations in this source file. Inference: Share a helper across same-file extensions without making it module-wide.
public var displayMessage: String? { (RealityKitImmersivePlayback/Extensions/VideoPlayerComponent+Extensions.swift:17) public The declaration is available to importing modules, subject to its containing type’s visibility. Inference: Satisfy a cross-target or framework-facing surface without implying subclassability.

No reviewed declaration uses open; unmodified Swift declarations are internal.

Reference code

RealityKitImmersivePlayback/Models/AppModel.swift:23 — representative visibility boundary.

    private(set) var selectedVideo: VideoModel?
    private(set) var videoModes: VideoModes?

    init() {
        // ...
    }

Logic ownership and placement

Logic Owning type or file Placement rationale
Scene declaration and dependency lifetime RealityKitImmersivePlaybackApp The App/entry boundary determines window, volume, and immersive-space lifetime.
Presentation, attachments, and gestures ProgressivePlayerImmersiveSpace 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.
Playback resource lifecycle RealityKitImmersivePlayback/Models/PlayerModel.swift:14 The player/component owner survives view recomputation and drives system presentation.

Design patterns

Pattern Source evidence Purpose or tradeoff
SwiftUI scene composition RealityKitImmersivePlayback/RealityKitImmersivePlaybackApp.swift:11 Keeps windows, volumes, and immersive-space lifecycle visible at the app boundary.
SwiftUI–RealityKit bridge RealityKitImmersivePlayback/Views/LoadingIndicator.swift:49 Builds and updates a RealityKit entity graph from SwiftUI lifecycle closures.
Explicit immersive-space lifecycle RealityKitImmersivePlayback/RealityKitImmersivePlaybackApp.swift:14 Makes immersive presentation a scene transition rather than hidden global state.
Observable state owner RealityKitImmersivePlayback/Models/AppModel.swift:15 Shares feature state across multiple views without moving framework resources into view values.
Async update consumer RealityKitImmersivePlayback/Models/PlayerModel.swift:73 Consumes provider or event streams in cancellable structured tasks.

Naming conventions

  • Role suffixes are evidence, not decoration: App: RealityKitImmersivePlaybackApp; Model: AppModel, PlayerModel, VideoModel; View: ContentView, ImmersiveControlsView, TransportView, VideoPlayerView.
  • Protocols: no app-defined protocol in the reviewed source.
  • Commands use verb-led methods: closeImmersivePlayer, makeDefaultScenes, openImmersivePlayer, transitionStageFromBrowsingToPlayback, transitionStageFromPlaybackToBrowsing, transitionPlaybackStages, transitionStage, loadItem.
  • Files generally match their primary type; Views, Models, Managers, Providers, Components, Systems, and Packages folders describe architectural roles where present.

Architecture takeaways

  • Treat RealityKitImmersivePlaybackApp as 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.
  • Model immersive-space open, transition, and close states explicitly so windows and immersive content cannot drift apart.

Source map

Source file Relevant symbols
RealityKitImmersivePlayback/RealityKitImmersivePlaybackApp.swift:11 RealityKitImmersivePlaybackApp
RealityKitImmersivePlayback/Models/PlayerModel.swift:13 PlayerModel
RealityKitImmersivePlayback/Models/AppModel.swift:15 AppModel, ImmersiveSpaceState, WindowState, PlaybackScene, Stage
RealityKitImmersivePlayback/Models/VideoModel.swift:11 VideoModel, ContentType, Mode
RealityKitImmersivePlayback/Scenes/PlayerImmersiveSpace.swift:12 PlayerImmersiveSpace, ProgressivePlayerImmersiveSpace, SpatialPlayerImmersiveSpace
RealityKitImmersivePlayback/Views/ContentView.swift:10 ContentView
RealityKitImmersivePlayback/Views/ImmersiveControlsView.swift:11 ImmersiveControlsView
RealityKitImmersivePlayback/Views/LoadingIndicator.swift:13 LoadingIndicator, LoadingIndicatorOverlay, LoadingIndicatorSpatialOverlay, LoadingIndicatorSceneOverlayModifier