Tracking a handheld accessory as a virtual sculpting tool
At a glance
| Item | Summary |
|---|---|
| Purpose | Use a tracked accessory with Apple Vision Pro to create a virtual sculpture. |
| App architecture | SpatialSculptingApp owns the document scene; SculptingToolModel maps ARKit accessory updates into tool pose and haptics, while compute and sculpting systems modify the volume document’s low-level resources. |
| Main patterns | SwiftUI scene composition, UI–RealityKit bridge, Observable state owner, Entity-component-system, Protocol capability boundary, Tracked-input compute pipeline |
| Project style | Code-rich sample with 25 scanned C/Objective-C header, Metal, Swift file(s) and 2033 implementation line(s). |
Project structure
Source bundle/
└── SpatialSculpting/
└── SpatialSculpting/
├── ViewModel/
│ ├── SculptingToolModel.swift # SculptingToolModel
│ └── HapticsModel.swift # HapticsModel
├── Document/
│ └── VolumeDocument.swift # VolumeDocumentError, VolumeDocument
├── SpatialSculptingApp.swift # SpatialSculptingApp
├── Compute/
│ └── ComputeSystem.swift # ComputeUpdateContext, ComputeSystem, ComputeSystemComponent
├── ContentView.swift # ContentView
└── ECS/
└── SculptingToolComponent.swift # SculptingMode, SculptingToolComponent, SculptingToolSystem
Structure observations
- The runtime boundary is WindowGroup + volumetric window + SwiftUI/UIKit host; 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.
Overall architecture
flowchart LR
SpatialSculptingApp_1["SpatialSculptingApp"]
WindowGroup___volumetric_window___SwiftUI_UIKit_host_2["WindowGroup + volumetric window + SwiftUI/UIKit host"]
ContentView_3["ContentView"]
SculptingToolModel_4["SculptingToolModel"]
SculptingToolSystem_5["SculptingToolSystem"]
RealityKit_entity_graph_6["RealityKit entity graph"]
accessory_tracking___compute_sculpting_7["accessory tracking + compute sculpting"]
SpatialSculptingApp_1 --> WindowGroup___volumetric_window___SwiftUI_UIKit_host_2
WindowGroup___volumetric_window___SwiftUI_UIKit_host_2 --> ContentView_3
ContentView_3 --> SculptingToolModel_4
SculptingToolModel_4 --> SculptingToolSystem_5
SculptingToolSystem_5 --> RealityKit_entity_graph_6
RealityKit_entity_graph_6 --> accessory_tracking___compute_sculpting_7
Reference code
SpatialSculpting/SpatialSculpting/SpatialSculptingApp.swift:11 — executable or app-lifecycle anchor.
struct SpatialSculptingApp: 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
classDiagram
ContentView *-- VolumeDocument : saveDocument
SculptingToolModel --> Entity : rootEntity
SculptingToolModel --> AnchorEntity : sculptingEntity
SculptingToolModel --> ModelEntity : trackingStateIndicator
Ownership evidence
SpatialSculpting/SpatialSculpting/ContentView.swift:23 — representative stored state or nearest verified lifecycle anchor.
struct ContentView: View {
// ...
@State var saveDocument: VolumeDocument? = nil
// ...
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
ContentView |
VolumeDocument as saveDocument |
owns SwiftUI value state | The owning type coordinates writes |
SculptingToolModel |
Entity as rootEntity |
stores and coordinates | The owning type coordinates writes |
SculptingToolModel |
AnchorEntity as sculptingEntity |
stores and coordinates | The owning type coordinates writes |
SculptingToolModel |
ModelEntity as trackingStateIndicator |
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 |
|---|---|---|
SpatialSculptingApp (SpatialSculpting/SpatialSculpting/SpatialSculptingApp.swift:11) |
Declares app lifecycle and top-level dependency lifetime. | App |
ContentView (SpatialSculpting/SpatialSculpting/ContentView.swift:14) |
Presents UI and forwards gestures or lifecycle events. | View |
SculptingToolModel (SpatialSculpting/SpatialSculpting/ViewModel/SculptingToolModel.swift:13) |
Owns feature state and domain transitions. | Concrete framework collaborators |
SculptingToolSystem (SpatialSculpting/SpatialSculpting/ECS/SculptingToolComponent.swift:38) |
Updates entities matching a RealityKit component query. | ComputeSystem |
ComputeSystem (SpatialSculpting/SpatialSculpting/Compute/ComputeSystem.swift:55) |
Declares a replaceable capability or collaboration contract. | Concrete framework collaborators |
ComputeSystemComponent (SpatialSculpting/SpatialSculpting/Compute/ComputeSystem.swift:61) |
Stores RealityKit entity data. | Component |
ComputeDispatchSystem (SpatialSculpting/SpatialSculpting/Compute/ComputeSystem.swift:66) |
Updates entities matching a RealityKit component query. | System |
SculptingToolComponent (SpatialSculpting/SpatialSculpting/ECS/SculptingToolComponent.swift:22) |
Stores RealityKit entity data. | Component |
Verified protocol-oriented seams: SculptingToolSystem → ComputeSystem. Framework conformances remain adapters, not proof of an app-wide protocol architecture.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
private let marchPipeline: MTLComputePipelineState = makeComputePipelin... (SpatialSculpting/SpatialSculpting/Mesh/MarchingCubesMesh.swift:27) |
private |
Use stays inside the declaration and same-file extensions permitted by Swift. | Inference: Hide lifecycle-sensitive state or an implementation step. |
public static let triangleTable: [UInt64] = [ (SpatialSculpting/SpatialSculpting/Mesh/TriangleTable.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. |
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
SpatialSculpting/SpatialSculpting/Mesh/MarchingCubesMesh.swift:27 — representative visibility boundary.
private let marchPipeline: MTLComputePipelineState = makeComputePipeline(named: "march")!
// Compute pipeline corresponding to the Metal compute shader function `clear`.
//
// See `MarchingCubesCompute.metal`.
private let clearPipeline: MTLComputePipelineState = makeComputePipeline(named: "clear")!Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application/command lifecycle | SpatialSculptingApp |
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 | SculptingToolModel |
A stable owner prevents view recomputation or callbacks from duplicating transitions. |
| Framework/session/render coordination | SculptingToolSystem |
The role-named collaborator isolates long-lived or per-frame work from presentation code. |
| Per-entity data and repeated behavior | SpatialSculpting/SpatialSculpting/Compute/ComputeSystem.swift:66 |
Components carry data; the system queries and updates matching entities. |
| GPU and low-level resource work | SpatialSculpting/SpatialSculpting/Mesh/MarchingCubesMesh.swift:11 |
Buffer, texture, shader, or frame-resource mutation stays in a rendering/compute boundary. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| SwiftUI scene composition | SpatialSculpting/SpatialSculpting/SpatialSculptingApp.swift:11 |
Keeps windows, volumes, and immersive presentation visible at the app boundary. |
| UI–RealityKit bridge | SpatialSculpting/SpatialSculpting/ContentView.swift:51 |
Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle. |
| Observable state owner | SpatialSculpting/SpatialSculpting/ViewModel/SculptingToolModel.swift:13 |
Keeps shared feature state in a stable owner rather than in transient view values. |
| Entity-component-system | SpatialSculpting/SpatialSculpting/Compute/ComputeSystem.swift:66 |
Stores per-entity data in components and advances repeated behavior in registered systems. |
| Protocol capability boundary | SpatialSculpting/SpatialSculpting/ECS/SculptingToolComponent.swift:38 |
Defines a local capability with concrete conformers; this is the verified protocol-oriented seam. |
| Tracked-input compute pipeline | SpatialSculpting/SpatialSculpting/ViewModel/SculptingToolModel.swift:13 |
Maps accessory tracking and haptics into tool state before compute systems modify the sculpted volume. |
Naming conventions
- Role suffixes are evidence, not decoration: App:
SpatialSculptingApp; Model:HapticsModel,SculptingToolModel; Document:VolumeDocument; System:ComputeDispatchSystem,ComputeSystem,SculptingToolSystem; Component:ComputeSystemComponent,SculptingToolComponent. - ECS names pair data and behavior: components
ComputeSystemComponent,SculptingToolComponent; systemsComputeDispatchSystem,SculptingToolSystem. - Protocols:
ComputeSystem. - Commands use verb-led methods:
selectToolbarElement,displayToolbar,handlePalettePress,updateTrackingStateIndicatorIfDirty,updateSculptingTool,addEntityAttachmentToRoot,loadFromURL,fileWrapper. - 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
SpatialSculptingAppas 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.
Source map
| Source file | Relevant symbols |
|---|---|
SpatialSculpting/SpatialSculpting/ViewModel/SculptingToolModel.swift:13 |
SculptingToolModel |
SpatialSculpting/SpatialSculpting/Document/VolumeDocument.swift:11 |
VolumeDocumentError, VolumeDocument |
SpatialSculpting/SpatialSculpting/SpatialSculptingApp.swift:11 |
SpatialSculptingApp |
SpatialSculpting/SpatialSculpting/Compute/ComputeSystem.swift:12 |
ComputeUpdateContext, ComputeSystem, ComputeSystemComponent, ComputeDispatchSystem |
SpatialSculpting/SpatialSculpting/ContentView.swift:14 |
ContentView |
SpatialSculpting/SpatialSculpting/ECS/SculptingToolComponent.swift:12 |
SculptingMode, SculptingToolComponent, SculptingToolSystem |
SpatialSculpting/SpatialSculpting/ViewModel/HapticsModel.swift:15 |
HapticsModel |