Sample CodevisionOSReviewed 2026-07-21View on Apple Developer

Docking a video player in an immersive scene

At a glance

Item Summary
Purpose Secure a video player in an immersive scene with a docking region you can specify.
App architecture DockingApp owns scene state while AVPlayerViewModel owns player and docking transitions; AVPlayerView maps that stable owner into windowed and immersive RealityKit presentation.
Main patterns SwiftUI scene composition, UI–RealityKit bridge, Observable state owner, Delegate callback adapter, Authored-content boundary, Player view-model lifecycle
Project style Code-light sample with 9 scanned Swift file(s) and 288 implementation line(s).

Project structure

Source bundle/
├── Models/AVPlayerViewModel.swift  # AVPlayerViewModel
├── Views/AVPlayerView.swift  # AVPlayerView
├── App/DockingApp.swift  # DockingApp
├── Models/AppModel.swift  # AppModel, ImmersiveSpaceState
├── Views/ContentView.swift  # ContentView
├── Views/ImmersivePickerView.swift  # ImmersivePickerView
├── Views/ImmersiveView.swift  # ImmersiveView
└── Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json  # authored RealityKit content

Structure observations

  • The runtime boundary is WindowGroup + ImmersiveSpace; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
  • The source is Swift; its compact size does not erase any explicit owner shown below.
  • Authored .reality, .rkassets, or Reality Composer Pro content is an implementation boundary; Swift loads or drives it rather than reproducing its entity graph.

Overall architecture

Reference code

App/DockingApp.swift:11 — executable or app-lifecycle anchor.

struct DockingApp: App {
    // ...
}

The diagram is a responsibility flow, not a claim that every adjacent node directly calls the next. It separates lifecycle, presentation, stable state, framework/session work, and the RealityKit entity graph.

Ownership and state

Ownership evidence

Models/AVPlayerViewModel.swift:14 — representative stored state or nearest verified lifecycle anchor.

@MainActor
@Observable
class AVPlayerViewModel: NSObject {
    // ...
    private var avPlayerViewController: AVPlayerViewController?
    // ...
}
Owner Object or state Relationship Mutation authority
AVPlayerViewModel AVPlayerViewController as avPlayerViewController stores and coordinates The declaring type controls writes
AVPlayerViewModel AVPlayer as avPlayer creates and retains The declaring type controls writes
DockingApp AppModel as appModel creates and retains The declaring type controls writes
DockingApp AVPlayerViewModel as avPlayerViewModel creates and retains The declaring type controls writes

Ownership is intentionally narrow: environment, bindings, observed objects, weak links, and delegate callbacks do not prove lifetime ownership. Initialized stored services and wrapper-managed state do; entities added inside a RealityKit content closure belong to that entity graph.

Class and protocol design

Type Responsibility Depends on or conforms to
DockingApp (App/DockingApp.swift:11) Declares app lifecycle and top-level dependency lifetime. App
AVPlayerView (Views/AVPlayerView.swift:10) Presents UI and forwards gestures or lifecycle events. UIViewControllerRepresentable
AVPlayerViewModel (Models/AVPlayerViewModel.swift:12) Owns UI-facing state and commands. NSObject
AppModel (Models/AppModel.swift:13) Owns feature state and domain transitions. Concrete framework collaborators
ContentView (Views/ContentView.swift:11) Presents UI and forwards gestures or lifecycle events. View
ImmersivePickerView (Views/ImmersivePickerView.swift:10) Presents UI and forwards gestures or lifecycle events. View
ImmersiveView (Views/ImmersiveView.swift:12) Presents UI and forwards gestures or lifecycle events. View

The reviewed source defines no local substitution protocol. Its App, View, delegate, renderer, ARKit, and RealityKit conformances are framework-facing contracts, so this document does not label the whole app protocol-oriented.

Access control

Symbol Access Verified effect Likely rationale
@State private var appModel = AppModel() (App/DockingApp.swift:13) private Use stays inside the declaration and same-file extensions permitted by Swift. Inference: Hide lifecycle-sensitive state or an implementation step.
public let realityKitContentBundle = Bundle.module (Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift:11) public The symbol is available to importing modules, subject to its containing type’s visibility. Inference: Expose an intentional package or cross-target surface.

No reviewed Swift access example uses fileprivate, private(set), open; declarations without a modifier are internal.

Reference code

App/DockingApp.swift:13 — representative visibility boundary.

@main
struct DockingApp: App {
    // ...
    @State private var appModel = AppModel()
    // ...
}

Logic ownership and placement

Logic Owning type or file Placement rationale
Application/command lifecycle DockingApp The executable boundary determines app, controller, scene, or command lifetime.
Presentation and user intent AVPlayerView The view/controller forwards input and owns only presentation-local work.
Shared feature state and commands AVPlayerViewModel A stable owner prevents view recomputation or callbacks from duplicating transitions.
Authored scene composition Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json Reality Composer Pro owns hierarchy, materials, animation, or behavior that Swift loads and coordinates.

Design patterns

Pattern Source evidence Purpose or tradeoff
SwiftUI scene composition App/DockingApp.swift:11 Keeps windows, volumes, and immersive presentation visible at the app boundary.
UI–RealityKit bridge Views/ImmersiveView.swift:16 Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle.
Observable state owner Models/AVPlayerViewModel.swift:12 Keeps shared feature state in a stable owner rather than in transient view values.
Delegate callback adapter Models/AVPlayerViewModel.swift:45 Inverts imperative framework callbacks into the sample’s owning controller, coordinator, or model.
Authored-content boundary Views/ImmersiveView.swift:10 Treats Reality Composer Pro assets as implementation and keeps Swift responsible for loading and runtime coordination.
Player view-model lifecycle Models/AVPlayerViewModel.swift:12 Keeps the player and dock/undock state stable while views move presentation between scenes.

Naming conventions

  • Role suffixes are evidence, not decoration: App: DockingApp; ViewModel: AVPlayerViewModel; Model: AppModel; View: AVPlayerView, ContentView, ImmersivePickerView, ImmersiveView.
  • Protocols: no app-defined protocol in the reviewed source.
  • Commands use verb-led methods: makePlayerViewController, play, reset, playerViewController, makeUIViewController, updateUIViewController.
  • Files and folders use primary-type or responsibility names such as Views, Models, Rendering, Components, Systems, Packages, or feature names where those boundaries exist.

Architecture takeaways

  • Treat DockingApp as the owner of executable or scene lifecycle, not automatically as the owner of every entity created later.
  • Keep view/controller input near presentation, but move sessions, reconstruction, capture, rendering, shared game state, or documents into stable owners when their lifetime exceeds one callback or render pass.
  • Count authored Reality Composer Pro assets as implementation; the Swift shell is not the complete architecture.
  • The compact source does not justify repository, coordinator, or protocol layers beyond boundaries the sample actually declares.

Source map

Source file Relevant symbols
Models/AVPlayerViewModel.swift:12 AVPlayerViewModel
Views/AVPlayerView.swift:10 AVPlayerView
App/DockingApp.swift:11 DockingApp
Models/AppModel.swift:13 AppModel, ImmersiveSpaceState
Views/ContentView.swift:11 ContentView
Views/ImmersivePickerView.swift:10 ImmersivePickerView
Views/ImmersiveView.swift:12 ImmersiveView