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

Bringing your SceneKit projects to RealityKit

At a glance

Item Summary
Purpose Adapt a platformer game for RealityKit’s powerful ECS and modularity.
App architecture PyroPandaRealityKitApp and AppModel coordinate the migrated game while feature packages split controller input, character movement, agents, attacks, and other behavior into RealityKit components and systems.
Main patterns SwiftUI scene composition, UI–RealityKit bridge, Observable state owner, Entity-component-system, Delegate callback adapter, Feature-package ECS migration
Project style Code-rich sample with 79 scanned Swift file(s) and 8518 implementation line(s).

Project structure

Source bundle/
├── Pyro Panda RealityKit/Pyro Panda RealityKit/PyroPandaRealityKitApp.swift  # PyroPandaRealityKitApp
├── Pyro Panda RealityKit/Pyro Panda RealityKit/AppModel.swift  # AppModel, ImmersiveSpaceState
├── Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentSystem.swift  # AgentSystem
├── Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentComponent.swift  # AgentComponent, AgentType, AgentState
├── Pyro Panda RealityKit/Pyro Panda RealityKit/Views/ContentView.swift  # ContentView
├── Pyro Panda SceneKit/Swift/Shared/UI/Menu.swift  # MenuDelegate, Menu
├── Pyro Panda SceneKit/Swift/iOS/ButtonOverlay.swift  # ButtonOverlayDelegate, ButtonOverlay
└── Pyro Panda RealityKit/Packages/PyroPanda/Package.realitycomposerpro/ProjectData/main.json  # authored RealityKit content

Structure observations

  • The runtime boundary is WindowGroup + ImmersiveSpace + SwiftUI/UIKit host; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
  • The source is 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

Pyro Panda RealityKit/Pyro Panda RealityKit/PyroPandaRealityKitApp.swift:13 — executable or app-lifecycle anchor.

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

Pyro Panda RealityKit/Pyro Panda RealityKit/PyroPandaRealityKitApp.swift:15 — representative stored state or nearest verified lifecycle anchor.

@main
struct PyroPandaRealityKitApp: App {
    // ...
    @State private var appModel = AppModel()
    // ...
}
Owner Object or state Relationship Mutation authority
PyroPandaRealityKitApp AppModel as appModel creates and retains The declaring type controls writes
AppModel Entity as gameRoot stores and coordinates The owning type coordinates writes
ContentView AppModel as appModel receives a shared or non-owning reference The upstream owner controls lifetime; this scope may invoke the exposed mutable API
AppModel ImmersiveSpaceState as immersiveSpaceState owns value state The owning type coordinates 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
PyroPandaRealityKitApp (Pyro Panda RealityKit/Pyro Panda RealityKit/PyroPandaRealityKitApp.swift:13) Declares app lifecycle and top-level dependency lifetime. App
ContentView (Pyro Panda RealityKit/Pyro Panda RealityKit/Views/ContentView.swift:13) Presents UI and forwards gestures or lifecycle events. View
AppModel (Pyro Panda RealityKit/Pyro Panda RealityKit/AppModel.swift:15) Owns feature state and domain transitions. Concrete framework collaborators
AgentSystem (Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentSystem.swift:12) Updates entities matching a RealityKit component query. System
MenuDelegate (Pyro Panda SceneKit/Swift/Shared/UI/Menu.swift:10) Declares a replaceable capability or collaboration contract. NSObjectProtocol
ButtonOverlayDelegate (Pyro Panda SceneKit/Swift/iOS/ButtonOverlay.swift:10) Declares a replaceable capability or collaboration contract. NSObjectProtocol
PadOverlayDelegate (Pyro Panda SceneKit/Swift/iOS/PadOverlay.swift:11) Declares a replaceable capability or collaboration contract. NSObjectProtocol
AgentComponent (Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentComponent.swift:14) Stores RealityKit entity data. Component

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 static func registerSystem() { (Pyro Panda RealityKit/Packages/PyroPanda/Sources/PyroPanda/RunAwayComponent.swift:35) private Use stays inside the declaration and same-file extensions permitted by Swift. Inference: Hide lifecycle-sensitive state or an implementation step.
fileprivate func realityKitCameraEntity(context: SceneUpdateContext) ->... (Pyro Panda RealityKit/Packages/CharacterMovement/Sources/CharacterMovement/CharacterMovementComponent.swift:119) fileprivate Use is limited to declarations in this source file. Inference: Share with same-file helpers without making the symbol module-wide.
public var state: AgentState? (Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentComponent.swift:33) public The symbol is available to importing modules, subject to its containing type’s visibility. Inference: Expose an intentional package or cross-target surface.
internal var wanderSpeed: Float = 5.0 (Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentComponent.swift:86) internal With no narrower modifier, Swift keeps the declaration internal to the module. Inference: Allow app-target collaboration without exporting a library API.

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

Reference code

Pyro Panda RealityKit/Packages/PyroPanda/Sources/PyroPanda/RunAwayComponent.swift:35 — representative visibility boundary.

    private static func registerSystem() {
        Task {
            await RunAwaySystem.registerSystem()
        }
    }

Logic ownership and placement

Logic Owning type or file Placement rationale
Application/command lifecycle PyroPandaRealityKitApp The executable boundary determines app, controller, scene, or command lifetime.
Presentation and user intent ContentView 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 AgentSystem The role-named collaborator isolates long-lived or per-frame work from presentation code.
Per-entity data and repeated behavior Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentSystem.swift:12 Components carry data; the system queries and updates matching entities.
GPU and low-level resource work Pyro Panda RealityKit/Pyro Panda RealityKit/AppModel.swift:38 Buffer, texture, shader, or frame-resource mutation stays in a rendering/compute boundary.
Authored scene composition Pyro Panda RealityKit/Packages/PyroPanda/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 Pyro Panda RealityKit/Pyro Panda RealityKit/PyroPandaRealityKitApp.swift:13 Keeps windows, volumes, and immersive presentation visible at the app boundary.
UI–RealityKit bridge Pyro Panda RealityKit/Pyro Panda RealityKit/Views/PyroPandaView.swift:32 Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle.
Observable state owner Pyro Panda RealityKit/Pyro Panda RealityKit/AppModel.swift:15 Keeps shared feature state in a stable owner rather than in transient view values.
Entity-component-system Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentSystem.swift:12 Stores per-entity data in components and advances repeated behavior in registered systems.
Delegate callback adapter Pyro Panda SceneKit/Swift/iOS/AppDelegate.swift:11 Inverts imperative framework callbacks into the sample’s owning controller, coordinator, or model.
Feature-package ECS migration Pyro Panda RealityKit/Pyro Panda RealityKit/PyroPandaRealityKitApp.swift:110 Splits migrated game behavior into feature packages and component/system pairs instead of one scene delegate.

Naming conventions

  • Role suffixes are evidence, not decoration: App: PyroPandaRealityKitApp; Model: AppModel; Controller: GameController, WASDController; System: AgentSystem, CharacterMovementSystem, ControllerInputSystem, FollowSystem, MoveBetweenSystem; Component: AgentComponent, AttackComponent, BaseComponent, CharacterMovementComponent, CharacterStateComponent.
  • ECS names pair data and behavior: components AgentComponent, CharacterMovementComponent, CharacterStateComponent, ControllerInputReceiver, RunAwayComponent; systems AgentSystem, CharacterMovementSystem, ControllerInputSystem, RunAwaySystem, SpinSystem.
  • Protocols: MenuDelegate, ButtonOverlayDelegate, PadOverlayDelegate.
  • Commands use verb-led methods: reset, didConnectController, didDisconnectController, update, constrainPosition, updateAgentState, fearingUpdate, chasingUpdate.
  • 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 PyroPandaRealityKitApp 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.
  • Count authored Reality Composer Pro assets as implementation; the Swift shell is not the complete architecture.

Source map

Source file Relevant symbols
Pyro Panda RealityKit/Pyro Panda RealityKit/PyroPandaRealityKitApp.swift:13 PyroPandaRealityKitApp
Pyro Panda RealityKit/Pyro Panda RealityKit/AppModel.swift:15 AppModel, ImmersiveSpaceState
Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentSystem.swift:12 AgentSystem
Pyro Panda RealityKit/Packages/AgentComponent/Sources/AgentComponent/AgentComponent.swift:14 AgentComponent, AgentType, AgentState, ConstraintValue, PositionConstraints
Pyro Panda RealityKit/Pyro Panda RealityKit/Views/ContentView.swift:13 ContentView
Pyro Panda SceneKit/Swift/Shared/UI/Menu.swift:10 MenuDelegate, Menu
Pyro Panda SceneKit/Swift/iOS/ButtonOverlay.swift:10 ButtonOverlayDelegate, ButtonOverlay