Creating a photogrammetry command-line app
At a glance
| Item | Summary |
|---|---|
| Purpose | Generate 3D objects from images using RealityKit Object Capture. |
| App architecture | Top-level main.swift defines HelloPhotogrammetry, parses command options, creates a PhotogrammetrySession, submits one request, and consumes the session’s asynchronous output stream. |
| Main patterns | Session owner boundary, Async update consumer, Async session-output consumer |
| Project style | Code-light sample with 1 scanned Swift file(s) and 237 implementation line(s). |
Project structure
Source bundle/
└── HelloPhotogrammetry/
└── main.swift # HelloPhotogrammetry, IllegalOption
Structure observations
- The runtime boundary is top-level command; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
- The source is Swift; its compact size does not erase any explicit owner shown below.
Overall architecture
flowchart LR
HelloPhotogrammetry_1["HelloPhotogrammetry"]
top_level_command_2["top-level command"]
PhotogrammetrySession_3["PhotogrammetrySession"]
HelloPhotogrammetry_1 --> top_level_command_2
top_level_command_2 --> PhotogrammetrySession_3
Reference code
HelloPhotogrammetry/main.swift:19 — executable or app-lifecycle anchor.
struct HelloPhotogrammetry: ParsableCommand {
// ...
}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
HelloPhotogrammetry *-- CommandConfiguration : configuration
HelloPhotogrammetry --> Detail : detail
HelloPhotogrammetry --> SampleOrdering : sampleOrdering
HelloPhotogrammetry --> FeatureSensitivity : featureSensitivity
Ownership evidence
HelloPhotogrammetry/main.swift:24 — representative stored state or nearest verified lifecycle anchor.
public static let configuration = CommandConfiguration(
abstract: "Reconstructs 3D USDZ model from a folder of images.")
@Argument(help: "The local input file folder of images.")
private var inputFolder: String
@Argument(help: "Full path to the USDZ output file.")
private var outputFilename: String| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
HelloPhotogrammetry |
CommandConfiguration as configuration |
creates and retains | The owning type coordinates writes |
HelloPhotogrammetry |
Detail as detail |
stores and coordinates | The declaring type controls writes |
HelloPhotogrammetry |
SampleOrdering as sampleOrdering |
stores and coordinates | The declaring type controls writes |
HelloPhotogrammetry |
FeatureSensitivity as featureSensitivity |
stores and coordinates | 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 |
|---|---|---|
HelloPhotogrammetry (HelloPhotogrammetry/main.swift:19) |
Represents a feature value or composable behavior. | ParsableCommand |
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 let logger = Logger(subsystem: "com.apple.sample.photogrammetry", (HelloPhotogrammetry/main.swift:14) |
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 configuration = CommandConfiguration( (HelloPhotogrammetry/main.swift:24) |
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.
Reference code
HelloPhotogrammetry/main.swift:14 — representative visibility boundary.
private let logger = Logger(subsystem: "com.apple.sample.photogrammetry",
category: "HelloPhotogrammetry")Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application/command lifecycle | HelloPhotogrammetry |
The executable boundary determines app, controller, scene, or command lifetime. |
| Shared feature state and commands | HelloPhotogrammetry |
A stable owner prevents view recomputation or callbacks from duplicating transitions. |
| Capture/reconstruction session lifecycle | HelloPhotogrammetry/main.swift:21 |
Session creation and output handling stay outside render-only view code. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| Session owner boundary | HelloPhotogrammetry/main.swift:21 |
Keeps authorization, session lifetime, and framework callbacks in a stable model, manager, or controller. |
| Async update consumer | HelloPhotogrammetry/main.swift:79 |
Consumes long-lived framework output in a cancellable asynchronous task. |
| Async session-output consumer | HelloPhotogrammetry/main.swift:21 |
Streams progress, completion, and failures from a long-running photogrammetry session without a UI layer. |
Naming conventions
- Role suffixes are evidence, not decoration: feature nouns and framework type names dominate this compact sample.
- Protocols: no app-defined protocol in the reviewed source.
- Commands use verb-led methods:
run,makeConfigurationFromArguments,makeRequestFromArguments,handleRequestComplete,handleRequestProgress. - 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
HelloPhotogrammetryas 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.
- The compact source does not justify repository, coordinator, or protocol layers beyond boundaries the sample actually declares.
Source map
| Source file | Relevant symbols |
|---|---|
HelloPhotogrammetry/main.swift:19 |
HelloPhotogrammetry, IllegalOption |