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

Creating a game with scene understanding

At a glance

Item Summary
Purpose Create AR games and experiences that interact with real-world objects on LiDAR-equipped iOS devices.
App architecture The shared BugBreakAR implementation is viewed through its scene-understanding path: GameViewController configures AR reconstruction, and GameManager maps classified real-world surfaces into collision-aware gameplay.
Main patterns UIKit lifecycle composition, UI–RealityKit bridge, Session owner boundary, Protocol capability boundary, Delegate callback adapter, Scene-understanding adapter
Project style Code-rich sample with 59 scanned C/Objective-C header, Metal, Swift file(s) and 7988 implementation line(s).

Project structure

Source bundle/
└── BugBreakAR/
    ├── GameViewController.swift  # GameViewController
    ├── GameManager.swift  # GameManager, GameState
    ├── Components/
    │   ├── EntitySwitcherComponent.swift  # EntitySwitcherAnimationType, EntitySwitcherComponent, ChildEntityInfo
    │   ├── AudioComponent.swift  # AudioComponent, HasAudioComponent
    │   └── InteractionComponent.swift  # InteractionComponent, InteractionComponentDelegate, HasInteraction
    ├── AppDelegate.swift  # AppDelegate
    └── MainMenuViewController.swift  # MainMenuViewController

Structure observations

  • The runtime boundary is UIApplicationDelegate + UIViewController; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
  • The source is C/Objective-C header, Metal, Swift; role-named types and feature folders separate the major responsibilities.
  • This archive extracts to the same source-tree SHA-256 as Building an immersive experience with RealityKit; this document focuses on scene reconstruction and real-world collision integration.

Overall architecture

Reference code

BugBreakAR/AppDelegate.swift:13 — executable or app-lifecycle anchor.

class AppDelegate: UIResponder, UIApplicationDelegate {
    // ...
}

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

BugBreakAR/GameManager.swift:20 — representative stored state or nearest verified lifecycle anchor.

    weak var viewController: GameViewController?
    var inputSystemInstance: InputSystem?
    var voxels: Voxels?
    weak var assets: GameAssets?

    // Game state related
    var currentState = GameState.applicationLaunched
    var assetsLoaded = false
Owner Object or state Relationship Mutation authority
GameManager GameViewController as viewController receives a shared or non-owning reference The upstream owner controls lifetime; this scope may invoke the exposed mutable API
GameManager AnchorEntity as creaturesAnchor creates and retains The owning type coordinates writes
GameManager AnchorEntity as audioAnchor stores and coordinates The owning type coordinates writes
GameManager InputSystem as inputSystemInstance stores and coordinates 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
AppDelegate (BugBreakAR/AppDelegate.swift:13) Declares app lifecycle and top-level dependency lifetime. UIResponder, UIApplicationDelegate
GameViewController (BugBreakAR/GameViewController.swift:14) Coordinates long-lived framework or cross-view work. UIViewController, UIGestureRecognizerDelegate
GameManager (BugBreakAR/GameManager.swift:11) Coordinates long-lived framework or cross-view work. Concrete framework collaborators
HasAudioComponent (BugBreakAR/Components/AudioComponent.swift:16) Declares a replaceable capability or collaboration contract. Concrete framework collaborators
HasEntitySwitcher (BugBreakAR/Components/EntitySwitcherComponent.swift:66) Declares a replaceable capability or collaboration contract. Concrete framework collaborators
EntitySwitcherComponentDelegate (BugBreakAR/Components/EntitySwitcherComponent.swift:68) Declares a replaceable capability or collaboration contract. AnyObject
InteractionComponentDelegate (BugBreakAR/Components/InteractionComponent.swift:25) Declares a replaceable capability or collaboration contract. AnyObject
HasInteraction (BugBreakAR/Components/InteractionComponent.swift:31) Declares a replaceable capability or collaboration contract. Concrete framework collaborators

Verified protocol-oriented seams: DebugSettingBoolDebugSettingProtocol, DebugSettingIntDebugSettingProtocol, DebugSettingFloatDebugSettingProtocol, DebugSettingEnumDebugSettingProtocol, CreatureEntityHasEntitySwitcher, CreatureEntityEntitySwitcherComponentDelegate. Framework conformances remain adapters, not proof of an app-wide protocol architecture.

Access control

Symbol Access Verified effect Likely rationale
private let log = OSLog(subsystem: appSubsystem, category: "EntitySwitc... (BugBreakAR/Components/EntitySwitcherComponent.swift:15) private Use stays inside the declaration and same-file extensions permitted by Swift. Inference: Hide lifecycle-sensitive state or an implementation step.
fileprivate var childEntityInfoList = [ChildEntityInfo]() (BugBreakAR/Components/EntitySwitcherComponent.swift:48) fileprivate Use is limited to declarations in this source file. Inference: Share with same-file helpers without making the symbol module-wide.
public var audioComponent: AudioComponent { (BugBreakAR/Components/AudioComponent.swift:19) 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 func configurePathfinding() { (BugBreakAR/CreatureEntity+Pathfinding.swift:12) 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.

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

Reference code

BugBreakAR/Components/EntitySwitcherComponent.swift:15 — representative visibility boundary.

private let log = OSLog(subsystem: appSubsystem, category: "EntitySwitchComponent")

Logic ownership and placement

Logic Owning type or file Placement rationale
Application/command lifecycle AppDelegate The executable boundary determines app, controller, scene, or command lifetime.
Presentation and user intent GameViewController The view/controller forwards input and owns only presentation-local work.
Shared feature state and commands GameManager A stable owner prevents view recomputation or callbacks from duplicating transitions.
GPU and low-level resource work BugBreakAR/Rendering/MetalModel.swift:74 Buffer, texture, shader, or frame-resource mutation stays in a rendering/compute boundary.

Design patterns

Pattern Source evidence Purpose or tradeoff
UIKit lifecycle composition BugBreakAR/AppDelegate.swift:13 Uses the application delegate and view controller as the explicit lifecycle and scene-ownership boundary.
UI–RealityKit bridge BugBreakAR/MainMenuViewController.swift:24 Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle.
Session owner boundary BugBreakAR/GameViewController+ARSessionDelegate.swift:12 Keeps authorization, session lifetime, and framework callbacks in a stable model, manager, or controller.
Protocol capability boundary BugBreakAR/DebugSettings/DebugSettings.swift:27 Defines a local capability with concrete conformers; this is the verified protocol-oriented seam.
Delegate callback adapter BugBreakAR/AppDelegate.swift:13 Inverts imperative framework callbacks into the sample’s owning controller, coordinator, or model.
Scene-understanding adapter BugBreakAR/GameViewController.swift:86 Maps AR mesh/classification evidence into the collision-aware game world while keeping gameplay in its manager and systems.

Naming conventions

  • Role suffixes are evidence, not decoration: Model: MetalModel; Controller: DebugSettingsViewController, GameViewController, MainMenuViewController; Manager: GameManager; Renderer: MetalRenderer; System: InputSystem.
  • ECS names pair data and behavior: components AudioComponent, CustomCameraComponent, EntitySwitcherComponent, InteractionComponent, PathfindingComponent; systems none.
  • Protocols: HasAudioComponent, HasEntitySwitcher, EntitySwitcherComponentDelegate, InteractionComponentDelegate, HasInteraction, PathfindingComponentDelegate.
  • Commands use verb-led methods: viewWillAppear, viewDidAppear, prepare, configureView, resetTracking, setupIBL, gestureRecognizer, nearbyFaceWithClassification.
  • 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 AppDelegate 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.
  • A shared source tree can support different documentation questions; keep each page focused on its specific architecture question rather than duplicating generic analysis.

Source map

Source file Relevant symbols
BugBreakAR/GameViewController.swift:14 GameViewController
BugBreakAR/GameManager.swift:11 GameManager, GameState
BugBreakAR/Components/EntitySwitcherComponent.swift:18 EntitySwitcherAnimationType, EntitySwitcherComponent, ChildEntityInfo, HasEntitySwitcher, EntitySwitcherComponentDelegate
BugBreakAR/AppDelegate.swift:13 AppDelegate
BugBreakAR/Components/AudioComponent.swift:12 AudioComponent, HasAudioComponent
BugBreakAR/Components/InteractionComponent.swift:12 InteractionComponent, InteractionComponentDelegate, HasInteraction
BugBreakAR/MainMenuViewController.swift:14 MainMenuViewController