Placing content on detected planes
At a glance
| Item | Summary |
|---|---|
| Purpose | Detect horizontal surfaces like tables and floors, as well as vertical planes like walls and doors. |
| App architecture | ObjectPlacementApp composes WindowGroup + ImmersiveSpace around HomeView; AppState 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, Plane projection pipeline |
| Project style | Code-rich sample with 19 scanned Swift file(s) and 2083 Swift line(s); resources and generated assets are excluded from those counts. |
Project structure
Source bundle/
└── ObjectPlacement/
├── App/
│ ├── PlacementManager.swift # PlacementManager
│ └── AppState.swift # AppState
├── Utilities/
│ ├── PlaneAnchorHandler.swift # PlaneAnchorHandler
│ └── PersistenceManager.swift # PersistenceManager
├── Views/
│ ├── ObjectPlacementRealityView.swift # ObjectPlacementRealityView, Attachments
│ ├── HomeView.swift # HomeView
│ └── ObjectPlacementMenuView.swift # ObjectPlacementMenuView
└── ObjectPlacementApp.swift # UIIdentifier, ObjectPlacementApp
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.
- Uses the Object Placement archive but focuses on plane updates, ray projection, collision checks, and placement preview state.
Overall architecture
flowchart LR
ObjectPlacementApp_1["ObjectPlacementApp"]
WindowGroup___ImmersiveSpace_2["WindowGroup + ImmersiveSpace"]
HomeView_3["HomeView"]
AppState_4["AppState"]
RealityView_entity_graph_5["RealityView entity graph"]
ARKit_providers___RealityKit_6["ARKit providers + RealityKit"]
ObjectPlacementApp_1 --> WindowGroup___ImmersiveSpace_2
WindowGroup___ImmersiveSpace_2 --> HomeView_3
HomeView_3 --> AppState_4
AppState_4 --> RealityView_entity_graph_5
RealityView_entity_graph_5 --> ARKit_providers___RealityKit_6
Reference code
ObjectPlacement/ObjectPlacementApp.swift:16 — the app or executable entry declares the outer scene lifecycle.
struct ObjectPlacementApp: 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
classDiagram
ObjectPlacementApp *-- AppState : appState
ObjectPlacementApp *-- ModelLoader : modelLoader
ObjectPlacementApp o-- ScenePhase : scenePhase
HomeView --> AppState : appState
Ownership evidence
ObjectPlacement/ObjectPlacementApp.swift:17 — representative stored state or the nearest verified lifecycle anchor.
@State private var appState = AppState()| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
ObjectPlacementApp |
AppState as appState |
creates and retains | Only the declaring scope writes |
ObjectPlacementApp |
ModelLoader as modelLoader |
creates and retains | Only the declaring scope writes |
ObjectPlacementApp |
ScenePhase as scenePhase |
receives a shared/non-owning reference | The upstream owner controls lifetime; this scope may invoke its mutable API |
HomeView |
AppState as appState |
stores and coordinates | The owning type coordinates 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 |
|---|---|---|
ObjectPlacementApp (ObjectPlacement/ObjectPlacementApp.swift:16) |
Declares app scenes and top-level dependency lifetime. | App |
HomeView (ObjectPlacement/Views/HomeView.swift:11) |
Presents UI and forwards gestures or lifecycle events. | View |
AppState (ObjectPlacement/App/AppState.swift:14) |
Owns observable feature state and domain transitions. | Concrete framework collaborators |
PersistenceManager (ObjectPlacement/Utilities/PersistenceManager.swift:12) |
Coordinates long-lived framework or cross-view work. | @unchecked Sendable |
PlacementManager (ObjectPlacement/App/PlacementManager.swift:16) |
Coordinates long-lived framework or cross-view work. | Concrete framework collaborators |
ObjectPlacementMenuView (ObjectPlacement/Views/ObjectPlacementMenuView.swift:10) |
Presents UI and forwards gestures or lifecycle events. | View |
ObjectPlacementRealityView (ObjectPlacement/Views/ObjectPlacementRealityView.swift:12) |
Presents UI and forwards gestures or lifecycle events. | View |
The source defines no local substitution protocol in the reviewed boundary. Its protocol use is framework-facing (App, View, RealityKit/ARKit protocols, or platform adapters), so this document does not label the whole app protocol-oriented.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
private(set) weak var placementManager: PlacementManager? = nil (ObjectPlacement/App/AppState.swift:16) |
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 renderContent: ModelEntity (ObjectPlacement/App/PlaceableObject.swift:33) |
private |
Use is restricted to the declaration and same-file extensions permitted by Swift. | Inference: Hide implementation details and lifecycle-sensitive state. |
fileprivate var previewPlacementManager: PlacementManager? = nil (ObjectPlacement/App/AppState.swift:110) |
fileprivate |
Use is restricted to declarations in this source file. | Inference: Share a helper across same-file extensions without making it module-wide. |
public func asUInt32Array() -> [UInt32] { (ObjectPlacement/Utilities/GeometryUtilities.swift:64) |
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 open; unmodified Swift declarations are internal.
Reference code
ObjectPlacement/App/AppState.swift:16 — representative visibility boundary.
private(set) weak var placementManager: PlacementManager? = nil
private(set) var placeableObjectsByFileName: [String: PlaceableObject] = [:]
private(set) var modelDescriptors: [ModelDescriptor] = []
var selectedFileName: String?Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Scene declaration and dependency lifetime | ObjectPlacementApp |
The App/entry boundary determines window, volume, and immersive-space lifetime. |
| Presentation, attachments, and gestures | HomeView |
SwiftUI view code forwards user intent and RealityView lifecycle events. |
| Shared feature state and commands | AppState |
A role-named owner prevents sibling views from duplicating transitions. |
| Tracking authorization and update streams | ObjectPlacement/App/PlacementManager.swift:18 |
Provider lifetime and async updates remain outside render-only view code. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| SwiftUI scene composition | ObjectPlacement/ObjectPlacementApp.swift:16 |
Keeps windows, volumes, and immersive-space lifecycle visible at the app boundary. |
| SwiftUI–RealityKit bridge | ObjectPlacement/Views/ObjectPlacementRealityView.swift:27 |
Builds and updates a RealityKit entity graph from SwiftUI lifecycle closures. |
| Explicit immersive-space lifecycle | ObjectPlacement/ObjectPlacementApp.swift:20 |
Makes immersive presentation a scene transition rather than hidden global state. |
| Observable state owner | ObjectPlacement/App/AppState.swift:14 |
Shares feature state across multiple views without moving framework resources into view values. |
| Provider session boundary | ObjectPlacement/App/AppState.swift:52 |
Owns provider lifetime separately from the SwiftUI view tree. |
| Plane projection pipeline | ObjectPlacement/App/PlacementManager.swift:19 |
Turns plane updates and device pose into a collision-checked placement preview. |
Naming conventions
- Role suffixes are evidence, not decoration: App:
ObjectPlacementApp; Manager:PersistenceManager,PlacementManager; View:HomeView,ObjectPlacementMenuView,ObjectPlacementRealityView,ObjectSelectionView,TooltipView. - Protocols: no app-defined protocol in the reviewed source.
- Commands use verb-led methods:
saveWorldAnchorsObjectsMapToDisk,addPlacementTooltip,removeHighlightedObject,runARKitSession,collisionBegan,collisionEnded,select,processWorldAnchorUpdates. - Files generally match their primary type;
Views,Models,Managers,Providers,Components,Systems, andPackagesfolders describe architectural roles where present.
Architecture takeaways
- Treat
ObjectPlacementAppas 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 |
|---|---|
ObjectPlacement/App/PlacementManager.swift:16 |
PlacementManager |
ObjectPlacement/Utilities/PlaneAnchorHandler.swift:12 |
PlaneAnchorHandler |
ObjectPlacement/Views/ObjectPlacementRealityView.swift:12 |
ObjectPlacementRealityView, Attachments |
ObjectPlacement/ObjectPlacementApp.swift:10 |
UIIdentifier, ObjectPlacementApp |
ObjectPlacement/Views/HomeView.swift:11 |
HomeView |
ObjectPlacement/App/AppState.swift:14 |
AppState |
ObjectPlacement/Utilities/PersistenceManager.swift:12 |
PersistenceManager |
ObjectPlacement/Views/ObjectPlacementMenuView.swift:10 |
ObjectPlacementMenuView |