Sample CodeiOS, iPadOS, Mac Catalyst, visionOSReviewed 2026-07-21View on Apple Developer

Creating a Spaceship game

At a glance

Item Summary
Purpose Build an immersive game using RealityKit audio, simulation, and rendering features.
App architecture SpaceshipApp owns shared AppModel; view models coordinate hangar and immersive phases while RealityKit components and systems handle ships, asteroids, hand control, lighting, and spatial-audio simulation.
Main patterns SwiftUI scene composition, UI–RealityKit bridge, Observable state owner, Entity-component-system, Session owner boundary, Game-state plus ECS split
Project style Code-rich sample with 57 scanned C/Objective-C header, Objective-C++, Swift file(s) and 4639 implementation line(s).

Project structure

Source bundle/
├── Spaceship/SpaceshipApp.swift  # SpaceshipApp
├── Spaceship/AppModel.swift  # AppModel, GamePhase
├── Spaceship/ECS/SpaceshipControl/HandsShipControlProvider.swift  # HandTrackingComponent, Location, ThrottleLabelPlacementComponent
├── Spaceship/ViewModels/HangarViewModel.swift  # HangarViewModel
├── Spaceship/Views/ImmersiveView.swift  # ImmersiveView
├── Spaceship/ECS/AudioMaterial.swift  # AudioMaterialComponent, AudioMaterialLookupComponent, AudioMaterial
├── Spaceship/ECS/Closure.swift  # ClosureComponent, ClosureSystem
└── Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json  # authored RealityKit content

Structure observations

  • The runtime boundary is WindowGroup + volumetric window + ImmersiveSpace; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
  • The source is C/Objective-C header, Objective-C++, Swift; role-named types and feature folders separate the major responsibilities.
  • 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

Spaceship/SpaceshipApp.swift:12 — executable or app-lifecycle anchor.

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

Spaceship/SpaceshipApp.swift:14 — representative stored state or nearest verified lifecycle anchor.

@main
struct SpaceshipApp: App {
    // ...
    @State var appModel = AppModel()
    // ...
}
Owner Object or state Relationship Mutation authority
SpaceshipApp AppModel as appModel creates and retains The owning type coordinates writes
ImmersiveView ImmersiveViewModel as viewModel creates and retains The declaring type controls writes
HandsShipControlProviderSystem SpatialTrackingSession as session creates and retains The owning type coordinates writes
ImmersiveView AppModel as appModel receives a shared or non-owning reference The upstream owner controls lifetime; this scope may invoke the exposed mutable API

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
SpaceshipApp (Spaceship/SpaceshipApp.swift:12) Declares app lifecycle and top-level dependency lifetime. App
ImmersiveView (Spaceship/Views/ImmersiveView.swift:12) Presents UI and forwards gestures or lifecycle events. View
AppModel (Spaceship/AppModel.swift:12) Owns feature state and domain transitions. Concrete framework collaborators
HandsShipControlProviderSystem (Spaceship/ECS/SpaceshipControl/HandsShipControlProvider.swift:46) Updates entities matching a RealityKit component query. System
AudioMaterialComponent (Spaceship/ECS/AudioMaterial.swift:10) Stores RealityKit entity data. Component
AudioMaterialLookupComponent (Spaceship/ECS/AudioMaterial.swift:14) Stores RealityKit entity data. Component
ClosureComponent (Spaceship/ECS/Closure.swift:10) Stores RealityKit entity data. Component
ClosureSystem (Spaceship/ECS/Closure.swift:20) Updates entities matching a RealityKit component query. System

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
private func addMeshAnchor(_ meshAnchor: MeshAnchor, shape: ShapeResour... (Spaceship/Utility/SceneReconstruction.swift:52) 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.
@property (nonatomic, assign) float throttle; (Spaceship/AudioUnitTurbine/AudioUnitTurbine.h:26) header-visible Translation units importing the header can use the declaration. Inference: Publish the C/Objective-C or Metal contract needed across files.

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

Objective-C/C header and implementation visibility is documented separately from Swift lexical access control.

Reference code

Spaceship/Utility/SceneReconstruction.swift:52 — representative visibility boundary.

    private func addMeshAnchor(_ meshAnchor: MeshAnchor, shape: ShapeResource) {
        // ...
    }

Logic ownership and placement

Logic Owning type or file Placement rationale
Application/command lifecycle SpaceshipApp The executable boundary determines app, controller, scene, or command lifetime.
Presentation and user intent ImmersiveView The view/controller forwards input and owns only presentation-local work.
Shared feature state and commands AppModel A stable owner prevents view recomputation or callbacks from duplicating transitions.
Framework/session/render coordination HandsShipControlProviderSystem The role-named collaborator isolates long-lived or per-frame work from presentation code.
Per-entity data and repeated behavior Spaceship/ECS/Closure.swift:20 Components carry data; the system queries and updates matching entities.
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 Spaceship/SpaceshipApp.swift:12 Keeps windows, volumes, and immersive presentation visible at the app boundary.
UI–RealityKit bridge Spaceship/Views/HangarView.swift:27 Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle.
Observable state owner Spaceship/AppModel.swift:12 Keeps shared feature state in a stable owner rather than in transient view values.
Entity-component-system Spaceship/ECS/Closure.swift:20 Stores per-entity data in components and advances repeated behavior in registered systems.
Session owner boundary Spaceship/ViewModels/ImmersiveViewModel.swift:16 Keeps authorization, session lifetime, and framework callbacks in a stable model, manager, or controller.
Game-state plus ECS split Spaceship/ECS/SpaceshipControl/HandsShipControlProvider.swift:46 Keeps application and game phases in models and repeated ship, hand, asteroid, lighting, and audio behavior in components and systems.

Naming conventions

  • Role suffixes are evidence, not decoration: App: SpaceshipApp; ViewModel: HangarViewModel, ImmersiveViewModel; Model: AppModel; System: ClosureSystem, EnvironmentLightingFadeSystem, HandsShipControlProviderSystem, PlanetVisualsSystem, ShipAudioSystem; Component: AsteroidComponent, AudioMaterialComponent, AudioMaterialLookupComponent, CargoComponent, ClosureComponent.
  • ECS names pair data and behavior: components AudioMaterialComponent, AudioMaterialLookupComponent, ClosureComponent, PlanetVisualsComponent, AsteroidComponent; systems ClosureSystem, PlanetVisualsSystem, EnvironmentLightingFadeSystem, ShipAudioSystem, ShipFlightSystem.
  • Protocols: no app-defined protocol in the reviewed source.
  • Commands use verb-led methods: updateImmersion, computeTransformWith, update, updateShipControlParameters, interpolateThrottle, computeTargetThrottle, computePitchAndRoll, interpolate.
  • 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 SpaceshipApp 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.
  • Use components for per-entity data and systems for repeated simulation instead of centralizing every update in a view model or controller.
  • Tie asynchronous session/output consumers to an explicit owner so cancellation and teardown follow the same lifecycle.
  • Count authored Reality Composer Pro assets as implementation; the Swift shell is not the complete architecture.

Source map

Source file Relevant symbols
Spaceship/SpaceshipApp.swift:12 SpaceshipApp
Spaceship/AppModel.swift:12 AppModel, GamePhase
Spaceship/ECS/SpaceshipControl/HandsShipControlProvider.swift:12 HandTrackingComponent, Location, ThrottleLabelPlacementComponent, PitchRollLabelPlacementComponent, HandsShipControlProviderSystem
Spaceship/ViewModels/HangarViewModel.swift:14 HangarViewModel
Spaceship/Views/ImmersiveView.swift:12 ImmersiveView
Spaceship/ECS/AudioMaterial.swift:10 AudioMaterialComponent, AudioMaterialLookupComponent, AudioMaterial
Spaceship/ECS/Closure.swift:10 ClosureComponent, ClosureSystem