Sample CodevisionOSReviewed 2026-07-21View on Apple Developer

Creating a spatial drawing app with RealityKit

At a glance

Item Summary
Purpose Use low-level mesh and texture APIs to achieve fast updates to a person’s brush strokes by integrating RealityKit with ARKit and SwiftUI.
App architecture RealityKitDrawingApp owns window/space mode, DrawingDocument owns stroke state, AnchorEntityInputProvider supplies tracked input, and brush generators plus RealityKit systems update low-level meshes and textures.
Main patterns SwiftUI scene composition, UI–RealityKit bridge, Observable state owner, Entity-component-system, Protocol capability boundary, Document-input-render pipeline
Project style Code-rich sample with 46 scanned C/Objective-C header, Metal, Swift file(s) and 4242 implementation line(s).

Project structure

Source bundle/
├── RealityKitDrawingApp/RealityKitDrawingApp.swift  # RealityKitDrawingApp, Mode, SetModeKey
├── RealityKitDrawingApp/Document/DrawingDocument.swift  # Chirality, InputData, DrawingDocument
├── RealityKitDrawingApp/Document/AnchorEntityInputProvider.swift  # HandComponent, AnchorEntityInputProvider, DrawingSystem
├── RealityKitDrawingApp/Brushes/Solid/Style/SolidBrushStyleProvider.swift  # SolidBrushStyleProvider, ThicknessType, Settings
├── RealityKitDrawingApp/Canvas/DrawingCanvasPlacementView.swift  # DrawingCanvasPlacementView, DrawingCanvasPlacementComponent, DrawingCanvasPlacementSystem
├── RealityKitDrawingApp/Utilities/CurveProcessing.swift  # HermiteInterpolant, HermiteControlPoint, SubdivisionSearchItem
├── RealityKitDrawingApp/Brushes/Solid/SolidBrushComponent.swift  # SolidBrushComponent, SolidBrushSystem
└── RealityKitDrawingApp/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 C/Objective-C header, Metal, 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

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

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

RealityKitDrawingApp/Canvas/DrawingCanvasPlacementView.swift:22 — representative stored state or nearest verified lifecycle anchor.

struct DrawingCanvasPlacementView: View {
    // ...
    @State private var session: SpatialTrackingSession?
    // ...
}
Owner Object or state Relationship Mutation authority
DrawingCanvasPlacementView SpatialTrackingSession as session owns a SwiftUI-managed reference slot The declaring type controls writes
DrawingDocument Entity as rootEntity stores and coordinates The declaring type controls writes
RealityKitDrawingApp Mode as mode owns SwiftUI value state The declaring type controls writes
RealityKitDrawingApp BrushState as brushState 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
RealityKitDrawingApp (RealityKitDrawingApp/RealityKitDrawingApp.swift:12) Declares app lifecycle and top-level dependency lifetime. App
DrawingCanvasPlacementView (RealityKitDrawingApp/Canvas/DrawingCanvasPlacementView.swift:16) Presents UI and forwards gestures or lifecycle events. View
DrawingDocument (RealityKitDrawingApp/Document/DrawingDocument.swift:38) Owns feature state and domain transitions. Concrete framework collaborators
AnchorEntityInputProvider (RealityKitDrawingApp/Document/AnchorEntityInputProvider.swift:31) Wraps a framework, input, rendering, or session capability. Concrete framework collaborators
HermiteInterpolant (RealityKitDrawingApp/Utilities/CurveProcessing.swift:12) Declares a replaceable capability or collaboration contract. Concrete framework collaborators
SolidBrushComponent (RealityKitDrawingApp/Brushes/Solid/SolidBrushComponent.swift:10) Stores RealityKit entity data. Component
SolidBrushSystem (RealityKitDrawingApp/Brushes/Solid/SolidBrushComponent.swift:15) Updates entities matching a RealityKit component query. System
SparkleBrushComponent (RealityKitDrawingApp/Brushes/Sparkle/SparkleBrushComponent.swift:11) Stores RealityKit entity data. Component

Verified protocol-oriented seams: FloatHermiteInterpolant, SIMD2HermiteInterpolant, SIMD3HermiteInterpolant, SIMD4HermiteInterpolant. Framework conformances remain adapters, not proof of an app-wide protocol architecture.

Access control

Symbol Access Verified effect Likely rationale
private(set) var samples: [CurveSample] = [] (RealityKitDrawingApp/Brushes/Solid/CurveExtruder.swift:30) private(set) The getter keeps its wider visibility, while writes stay in the declaring type and permitted same-file extensions. Inference: Protect an invariant while allowing observation.
private static let query = EntityQuery(where: .has(SolidBrushComponent.... (RealityKitDrawingApp/Brushes/Solid/SolidBrushComponent.swift:16) private Use stays inside the declaration and same-file extensions permitted by Swift. Inference: Hide lifecycle-sensitive state or an implementation step.
fileprivate var windowId: String { (RealityKitDrawingApp/RealityKitDrawingApp.swift:32) fileprivate Use is limited to declarations in this source file. Inference: Share with same-file helpers without making the symbol module-wide.
public let flatness: Float (RealityKitDrawingApp/Brushes/Solid/CurveProcessing/SmoothCurveSampler.swift:20) 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 open; declarations without a modifier are internal.

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

Reference code

RealityKitDrawingApp/Brushes/Solid/CurveExtruder.swift:30 — representative visibility boundary.

    private(set) var samples: [CurveSample] = []
    
    /// The number of samples in `samples` that have been meshed in `lowLevelMesh`.
    private var cachedSampleCount: Int = 0
    
    /// The number of samples for which `lowLevelMesh` has capacity.
    @MainActor
    private var sampleCapacity: Int {
        // ...
    }

Logic ownership and placement

Logic Owning type or file Placement rationale
Application/command lifecycle RealityKitDrawingApp The executable boundary determines app, controller, scene, or command lifetime.
Presentation and user intent DrawingCanvasPlacementView The view/controller forwards input and owns only presentation-local work.
Shared feature state and commands DrawingDocument A stable owner prevents view recomputation or callbacks from duplicating transitions.
Framework/session/render coordination AnchorEntityInputProvider The role-named collaborator isolates long-lived or per-frame work from presentation code.
Per-entity data and repeated behavior RealityKitDrawingApp/Brushes/Solid/SolidBrushComponent.swift:15 Components carry data; the system queries and updates matching entities.
GPU and low-level resource work RealityKitDrawingApp/Splash Screen/SplashScreenBackgroundComponent.swift:7 Buffer, texture, shader, or frame-resource mutation stays in a rendering/compute boundary.
Authored scene composition RealityKitDrawingApp/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 RealityKitDrawingApp/RealityKitDrawingApp.swift:12 Keeps windows, volumes, and immersive presentation visible at the app boundary.
UI–RealityKit bridge RealityKitDrawingApp/Canvas/DrawingCanvasPlacementView.swift:62 Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle.
Observable state owner RealityKitDrawingApp/Document/DrawingDocument.swift:38 Keeps shared feature state in a stable owner rather than in transient view values.
Entity-component-system RealityKitDrawingApp/Brushes/Solid/SolidBrushComponent.swift:15 Stores per-entity data in components and advances repeated behavior in registered systems.
Protocol capability boundary RealityKitDrawingApp/Utilities/CurveProcessing.swift:23 Defines a local capability with concrete conformers; this is the verified protocol-oriented seam.
Document-input-render pipeline RealityKitDrawingApp/Document/DrawingDocument.swift:38 Separates durable stroke state, tracked input, and fast low-level mesh/texture generation.

Naming conventions

  • Role suffixes are evidence, not decoration: App: RealityKitDrawingApp; Document: DrawingDocument; Provider: AnchorEntityInputProvider, SolidBrushStyleProvider, SparkleBrushStyleProvider; System: DrawingCanvasPlacementSystem, DrawingCanvasVisualizationSystem, DrawingSystem, SolidBrushSystem, SparkleBrushSystem; Component: DrawingCanvasPlacementComponent, DrawingCanvasVisualizationComponent, HandComponent, SolidBrushComponent, SparkleBrushComponent.
  • ECS names pair data and behavior: components SolidBrushComponent, SparkleBrushComponent, DrawingCanvasPlacementComponent, DrawingCanvasVisualizationComponent, HandComponent; systems SolidBrushSystem, SparkleBrushSystem, DrawingCanvasPlacementSystem, DrawingCanvasVisualizationSystem, DrawingSystem.
  • Protocols: HermiteInterpolant.
  • Commands use verb-led methods: setMode, receive, update, radius, styleInput, distance, length, dot.
  • 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 RealityKitDrawingApp 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
RealityKitDrawingApp/RealityKitDrawingApp.swift:12 RealityKitDrawingApp, Mode, SetModeKey
RealityKitDrawingApp/Document/DrawingDocument.swift:14 Chirality, InputData, DrawingDocument
RealityKitDrawingApp/Document/AnchorEntityInputProvider.swift:11 HandComponent, AnchorEntityInputProvider, DrawingSystem
RealityKitDrawingApp/Brushes/Solid/Style/SolidBrushStyleProvider.swift:16 SolidBrushStyleProvider, ThicknessType, Settings
RealityKitDrawingApp/Canvas/DrawingCanvasPlacementView.swift:16 DrawingCanvasPlacementView, DrawingCanvasPlacementComponent, DrawingCanvasPlacementSystem
RealityKitDrawingApp/Utilities/CurveProcessing.swift:12 HermiteInterpolant, HermiteControlPoint, SubdivisionSearchItem
RealityKitDrawingApp/Brushes/Solid/SolidBrushComponent.swift:10 SolidBrushComponent, SolidBrushSystem