Building an object reconstruction app
At a glance
| Item | Summary |
|---|---|
| Purpose | Reconstruct objects from user-selected input images by using photogrammetry. |
| App architecture | ObjectCaptureReconstructionApp presents ContentView, which owns AppDataModel; the views collect input/output choices while the model creates a photogrammetry session, submits requests, and translates asynchronous outputs into reconstruction progress. |
| Main patterns | SwiftUI scene composition, UI–RealityKit bridge, Observable state owner, Session owner boundary, Async update consumer, Photogrammetry request pipeline |
| Project style | Code-rich sample with 30 scanned Swift file(s) and 1559 implementation line(s). |
Project structure
Source bundle/
└── ObjectCaptureReconstruction/
├── AppDataModel.swift # AppDataModel, State, DetailLevelOptionsUnderAdvancedMenu
├── ObjectCaptureReconstructionApp.swift # ObjectCaptureReconstructionApp
├── Settings/
│ ├── ReconstructionOptions/
│ │ ├── MaskingView.swift # MaskingView, MaskingOption
│ │ └── IgnoreBoundingBoxView.swift # IgnoreBoundingBoxView
│ └── FolderOptions/
│ └── FolderOptionsView.swift # FolderOptionsView
├── ContentView.swift # ContentView
└── Processing/
└── BottomBarView.swift # BottomBarView
Structure observations
- The runtime boundary is Window; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
- The source is Swift; role-named types and feature folders separate the major responsibilities.
Overall architecture
flowchart LR
ObjectCaptureReconstructionApp_1["ObjectCaptureReconstructionApp"]
Window_2["Window"]
ContentView_3["ContentView"]
AppDataModel_4["AppDataModel"]
RealityKit_entity_graph_5["RealityKit entity graph"]
Object_Capture_session_pipeline_6["Object Capture session pipeline"]
ObjectCaptureReconstructionApp_1 --> Window_2
Window_2 --> ContentView_3
ContentView_3 --> AppDataModel_4
AppDataModel_4 --> RealityKit_entity_graph_5
RealityKit_entity_graph_5 --> Object_Capture_session_pipeline_6
Reference code
ObjectCaptureReconstruction/ObjectCaptureReconstructionApp.swift:11 — executable or app-lifecycle anchor.
struct ObjectCaptureReconstructionApp: 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
AppDataModel --> PhotogrammetrySession : session
ContentView *-- AppDataModel : appDataModel
AppDataModel --> PhotogrammetrySession : detailLevelOptionUnderQualityMenu
AppDataModel --> Configuration : sessionConfiguration
Ownership evidence
ObjectCaptureReconstruction/AppDataModel.swift:24 — representative stored state or nearest verified lifecycle anchor.
private(set) var session: PhotogrammetrySession?
// App's current state.
var state: State = .ready {
// ...
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
AppDataModel |
PhotogrammetrySession as session |
stores and coordinates | The declaring type controls writes |
ContentView |
AppDataModel as appDataModel |
creates and retains | The declaring type controls writes |
AppDataModel |
PhotogrammetrySession as detailLevelOptionUnderQualityMenu |
stores and coordinates | The owning type coordinates writes |
AppDataModel |
Configuration as sessionConfiguration |
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 |
|---|---|---|
ObjectCaptureReconstructionApp (ObjectCaptureReconstruction/ObjectCaptureReconstructionApp.swift:11) |
Declares app lifecycle and top-level dependency lifetime. | App |
ContentView (ObjectCaptureReconstruction/ContentView.swift:10) |
Presents UI and forwards gestures or lifecycle events. | View |
AppDataModel (ObjectCaptureReconstruction/AppDataModel.swift:15) |
Owns feature state and domain transitions. | Concrete framework collaborators |
BottomBarView (ObjectCaptureReconstruction/Processing/BottomBarView.swift:15) |
Presents UI and forwards gestures or lifecycle events. | View |
FolderOptionsView (ObjectCaptureReconstruction/Settings/FolderOptions/FolderOptionsView.swift:11) |
Presents UI and forwards gestures or lifecycle events. | View |
IgnoreBoundingBoxView (ObjectCaptureReconstruction/Settings/ReconstructionOptions/IgnoreBoundingBoxView.swift:11) |
Presents UI and forwards gestures or lifecycle events. | View |
ImageFolderSelectionView (ObjectCaptureReconstruction/Settings/FolderOptions/ImageFolderSelectionView.swift:14) |
Presents UI and forwards gestures or lifecycle events. | View |
ImageFolderThumbnailView (ObjectCaptureReconstruction/Settings/FolderOptions/ImageFolderThumbnailView.swift:10) |
Presents UI and forwards gestures or lifecycle events. | View |
The reviewed source defines no local substitution protocol. Its App, View, delegate, renderer, ARKit, and RealityKit conformances are framework-facing contracts, so this document does not label the whole app protocol-oriented.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
private(set) var session: PhotogrammetrySession? (ObjectCaptureReconstruction/AppDataModel.swift:24) |
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 let logger = Logger(subsystem: ObjectCaptureReconstructionApp.s... (ObjectCaptureReconstruction/AppDataModel.swift:12) |
private |
Use stays inside the declaration and same-file extensions permitted by Swift. | Inference: Hide lifecycle-sensitive state or an implementation step. |
public var description: String { (ObjectCaptureReconstruction/AppDataModel.swift:149) |
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, open; declarations without a modifier are internal.
Reference code
ObjectCaptureReconstruction/AppDataModel.swift:24 — representative visibility boundary.
private(set) var session: PhotogrammetrySession?
// App's current state.
var state: State = .ready {
// ...
}Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application/command lifecycle | ObjectCaptureReconstructionApp |
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 | AppDataModel |
A stable owner prevents view recomputation or callbacks from duplicating transitions. |
| Capture/reconstruction session lifecycle | ObjectCaptureReconstruction/AppDataModel.swift:24 |
Session creation and output handling stay outside render-only view code. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| SwiftUI scene composition | ObjectCaptureReconstruction/ObjectCaptureReconstructionApp.swift:11 |
Keeps windows, volumes, and immersive presentation visible at the app boundary. |
| UI–RealityKit bridge | ObjectCaptureReconstruction/Processing/ModelView.swift:22 |
Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle. |
| Observable state owner | ObjectCaptureReconstruction/AppDataModel.swift:15 |
Keeps shared feature state in a stable owner rather than in transient view values. |
| Session owner boundary | ObjectCaptureReconstruction/AppDataModel.swift:24 |
Keeps authorization, session lifetime, and framework callbacks in a stable model, manager, or controller. |
| Async update consumer | ObjectCaptureReconstruction/Processing/ReconstructionProgressView.swift:93 |
Consumes long-lived framework output in a cancellable asynchronous task. |
| Photogrammetry request pipeline | ObjectCaptureReconstruction/AppDataModel.swift:24 |
Separates request creation, asynchronous output handling, and UI-facing reconstruction progress. |
Naming conventions
- Role suffixes are evidence, not decoration: App:
ObjectCaptureReconstructionApp; Model:AppDataModel; View:BottomBarView,ContentView,FolderOptionsView,IgnoreBoundingBoxView,ImageFolderSelectionView. - Protocols: no app-defined protocol in the reviewed source.
- Commands use verb-led methods:
startReconstruction,createSession,createReconstructionRequests,hash,getImageName,getFirstImage,isLoadableImageFile. - 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
ObjectCaptureReconstructionAppas 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.
- Tie asynchronous session/output consumers to an explicit owner so cancellation and teardown follow the same lifecycle.
Source map
| Source file | Relevant symbols |
|---|---|
ObjectCaptureReconstruction/AppDataModel.swift:15 |
AppDataModel, State, DetailLevelOptionsUnderAdvancedMenu |
ObjectCaptureReconstruction/ObjectCaptureReconstructionApp.swift:11 |
ObjectCaptureReconstructionApp |
ObjectCaptureReconstruction/Settings/ReconstructionOptions/MaskingView.swift:11 |
MaskingView, MaskingOption |
ObjectCaptureReconstruction/ContentView.swift:10 |
ContentView |
ObjectCaptureReconstruction/Processing/BottomBarView.swift:15 |
BottomBarView |
ObjectCaptureReconstruction/Settings/FolderOptions/FolderOptionsView.swift:11 |
FolderOptionsView |
ObjectCaptureReconstruction/Settings/ReconstructionOptions/IgnoreBoundingBoxView.swift:11 |
IgnoreBoundingBoxView |