Sample CodevisionOSReviewed 2026-07-21View on Apple Developer

Drawing in the air and on surfaces with a spatial stylus

At a glance

Item Summary
Purpose Create a spatial stylus drawing experience that balances latency and accuracy for both in-air and on-surface drawing.
App architecture SurfaceInkingApp composes WindowGroup + ImmersiveSpace around StylusDrawingSettingsView; AppModel coordinates ARKit provider updates and maps anchors into RealityKit content.
Main patterns SwiftUI scene composition, SwiftUI–RealityKit bridge, Explicit immersive-space lifecycle, Observable state owner, Provider session boundary, Async update consumer
Project style Code-rich sample with 26 scanned Swift file(s) and 3127 Swift line(s); resources and generated assets are excluded from those counts.

Project structure

Source bundle/
├── ARKit-SurfaceInking/ARKit-SurfaceInking/SurfaceInkingApp.swift  # SurfaceInkingApp
├── ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/DrawingInputProvider.swift  # StylusAnchorInput, DrawingInputProvider, TrackingMode
├── ARKit-SurfaceInking/ARKit-SurfaceInking/Models/AppModel.swift  # AppModel, ImmersiveSpaceState, DrawingSettings
├── ARKit-SurfaceInking/ARKit-SurfaceInking/Models/StylusModel.swift  # StylusButtonInput, Source, StylusModel
├── ARKit-SurfaceInking/ARKit-SurfaceInking/Views/StylusDrawingSettingsView.swift  # StylusDrawingSettingsView, StylusSettingsHeaderView, StylusSettingsBodyView
├── ARKit-SurfaceInking/ARKit-SurfaceInking/Utilities/CurveProcessing.swift  # HermiteInterpolant, HermiteControlPoint, SubdivisionSearchItem
├── ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/Mesh/BrushComponent.swift  # BrushComponent, BrushSystem
├── ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/Style/BrushStyleProvider.swift  # BrushStyleProvider, ThicknessType, Settings
└── ARKit-SurfaceInking/Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json  # authored RealityKit content

Structure observations

  • The runtime boundary is WindowGroup + ImmersiveSpace; the pruned tree lists only files that explain lifecycle, state, or framework integration.
  • App code is split into role-named views, models, managers, providers, components, or systems.
  • Authored .reality or Reality Composer Pro content is a real implementation boundary; Swift loads or drives it rather than reproducing its entity graph.

Overall architecture

Reference code

ARKit-SurfaceInking/ARKit-SurfaceInking/SurfaceInkingApp.swift:12 — the app or executable entry declares the outer scene lifecycle.

struct SurfaceInkingApp: App {
    // ...
}

The diagram is a responsibility flow, not a claim that every adjacent node directly calls the next. It keeps scene ownership, shared state, RealityKit content, and framework-provider work at separate levels.

Ownership and state

Ownership evidence

ARKit-SurfaceInking/ARKit-SurfaceInking/SurfaceInkingApp.swift:14 — representative stored state or the nearest verified lifecycle anchor.

@main
struct SurfaceInkingApp: App {
    // ...
    private let appModel = AppModel()
    // ...
}
Owner Object or state Relationship Mutation authority
SurfaceInkingApp AppModel as appModel creates and retains Only the declaring scope writes
SurfaceInkingApp StylusModel as stylusModel creates and retains Only the declaring scope writes
StylusDrawingSettingsView AppModel as appModel receives a shared/non-owning reference The upstream owner controls lifetime; this scope may invoke its mutable API
StylusInputState AppModel as appModel stores and coordinates Only the declaring scope writes

Ownership here is deliberately narrow: @Environment and weak references are shared links, initialized @State or stored services are lifecycle ownership, and a RealityView content closure owns additions to its entity graph without making the SwiftUI view a reference-type owner.

Class and protocol design

Type Responsibility Depends on or conforms to
SurfaceInkingApp (ARKit-SurfaceInking/ARKit-SurfaceInking/SurfaceInkingApp.swift:12) Declares app scenes and top-level dependency lifetime. App
StylusDrawingSettingsView (ARKit-SurfaceInking/ARKit-SurfaceInking/Views/StylusDrawingSettingsView.swift:11) Presents UI and forwards gestures or lifecycle events. View
AppModel (ARKit-SurfaceInking/ARKit-SurfaceInking/Models/AppModel.swift:16) Owns observable feature state and domain transitions. Concrete framework collaborators
HermiteInterpolant (ARKit-SurfaceInking/ARKit-SurfaceInking/Utilities/CurveProcessing.swift:12) Declares a replaceable capability or lifecycle contract. Concrete framework collaborators
BrushSystem (ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/Mesh/BrushComponent.swift:15) Updates matching RealityKit entities. System
StylusModel (ARKit-SurfaceInking/ARKit-SurfaceInking/Models/StylusModel.swift:31) Owns observable feature state and domain transitions. Concrete framework collaborators
BrushStyleProvider (ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/Style/BrushStyleProvider.swift:16) Wraps a framework or transport capability. Concrete framework collaborators

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 curve: DrawingMeshGenerator (ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/CurveProcessing/SmoothCurveSampler.swift:31) private(set) The getter keeps its wider visibility, while mutation stays inside the declaring type and its permitted same-file extensions. Inference: Protect a state invariant while allowing observation.
private var brushState: BrushState (ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/DrawingDocument.swift:24) private Use is restricted to the declaration and same-file extensions permitted by Swift. Inference: Hide implementation details and lifecycle-sensitive state.
public let flatness: Float (ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/CurveProcessing/SmoothCurveSampler.swift:36) public The declaration is available to importing modules, subject to its containing type’s visibility. Inference: Satisfy a cross-target or framework-facing surface without implying subclassability.

No reviewed declaration uses fileprivate, open; unmodified Swift declarations are internal.

Reference code

ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/CurveProcessing/SmoothCurveSampler.swift:31 — representative visibility boundary.

    private(set) var curve: DrawingMeshGenerator
    
    /// A parameter that determines how closely the samples the smooth curve sampler generates should conform to the ideal smoothed curve.
    ///
    /// Lower values of flatness results in more samples but a smoother curve.
    public let flatness: Float

Logic ownership and placement

Logic Owning type or file Placement rationale
Scene declaration and dependency lifetime SurfaceInkingApp The App/entry boundary determines window, volume, and immersive-space lifetime.
Presentation, attachments, and gestures StylusDrawingSettingsView SwiftUI view code forwards user intent and RealityView lifecycle events.
Shared feature state and commands AppModel A role-named owner prevents sibling views from duplicating transitions.
Tracking authorization and update streams ARKit-SurfaceInking/ARKit-SurfaceInking/Models/StylusModel.swift:37 Provider lifetime and async updates remain outside render-only view code.

Design patterns

Pattern Source evidence Purpose or tradeoff
SwiftUI scene composition ARKit-SurfaceInking/ARKit-SurfaceInking/SurfaceInkingApp.swift:12 Keeps windows, volumes, and immersive-space lifecycle visible at the app boundary.
SwiftUI–RealityKit bridge ARKit-SurfaceInking/ARKit-SurfaceInking/Views/ImmersiveDrawingView.swift:20 Builds and updates a RealityKit entity graph from SwiftUI lifecycle closures.
Explicit immersive-space lifecycle ARKit-SurfaceInking/ARKit-SurfaceInking/SurfaceInkingApp.swift:31 Makes immersive presentation a scene transition rather than hidden global state.
Observable state owner ARKit-SurfaceInking/ARKit-SurfaceInking/Models/AppModel.swift:16 Shares feature state across multiple views without moving framework resources into view values.
Provider session boundary ARKit-SurfaceInking/ARKit-SurfaceInking/Models/StylusModel.swift:37 Owns provider lifetime separately from the SwiftUI view tree.
Async update consumer ARKit-SurfaceInking/ARKit-SurfaceInking/Models/StylusModel.swift:53 Consumes provider or event streams in cancellable structured tasks.

Naming conventions

  • Role suffixes are evidence, not decoration: App: SurfaceInkingApp; Model: AppModel, StylusModel; Provider: BrushStyleProvider, DrawingInputProvider; View: BrushStyleView, BrushTypeView, DeveloperDrawingMediumSettingsView, DeveloperSettingsView, ImmersiveDrawingView.
  • ECS names pair data and behavior: components none; systems BrushSystem.
  • Protocols: HermiteInterpolant.
  • Commands use verb-led methods: isButtonPressed, getButtonPressure, getDrawingMedium, getChangedButtonInputIndex, updateButtonInputs, pruneButtonInputs, getCurrentDrawingMedium, getAnchorInput.
  • Files generally match their primary type; Views, Models, Managers, Providers, Components, Systems, and Packages folders describe architectural roles where present.

Architecture takeaways

  • Treat SurfaceInkingApp as the owner of scene declarations, not as the owner of every RealityKit entity created later.
  • Keep view-local interaction in SwiftUI, but move provider sessions, playback resources, shared game state, or transport state into a stable owner when their lifetime exceeds one render pass.
  • Run ARKit providers for the scene lifetime and consume their asynchronous updates in cancellable tasks; map anchors to entities at the boundary.
  • Model immersive-space open, transition, and close states explicitly so windows and immersive content cannot drift apart.

Source map

Source file Relevant symbols
ARKit-SurfaceInking/ARKit-SurfaceInking/SurfaceInkingApp.swift:12 SurfaceInkingApp
ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/DrawingInputProvider.swift:24 StylusAnchorInput, DrawingInputProvider, TrackingMode, CorrectionMode, DrawingMedium, StylusInputState
ARKit-SurfaceInking/ARKit-SurfaceInking/Models/AppModel.swift:16 AppModel, ImmersiveSpaceState, DrawingSettings, AnimationSettings
ARKit-SurfaceInking/ARKit-SurfaceInking/Models/StylusModel.swift:15 StylusButtonInput, Source, StylusModel
ARKit-SurfaceInking/ARKit-SurfaceInking/Views/StylusDrawingSettingsView.swift:11 StylusDrawingSettingsView, StylusSettingsHeaderView, StylusSettingsBodyView
ARKit-SurfaceInking/ARKit-SurfaceInking/Utilities/CurveProcessing.swift:12 HermiteInterpolant, HermiteControlPoint, SubdivisionSearchItem
ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/Mesh/BrushComponent.swift:10 BrushComponent, BrushSystem
ARKit-SurfaceInking/ARKit-SurfaceInking/Drawing/Style/BrushStyleProvider.swift:16 BrushStyleProvider, ThicknessType, Settings