Capturing depth using the LiDAR camera
At a glance
| Item | Summary |
|---|---|
| Purpose | Access the LiDAR camera on supporting devices to capture precise depth data. |
| App architecture | A Metal, Swift sample with the source-visible chain LiDARDepthApp → ContentView → MTKColorTextureCoordinator → AVFoundation APIs. |
| Main patterns | View-controller organization, Protocol-oriented abstraction, Delegate or data-source callbacks, Coordinator, Binding-based state propagation |
| Project style | 17 scanned source file(s) across Metal, Swift, organized around ranked entry, type, and file boundaries. |
| Execution model | Source-visible boundaries: DispatchQueue(label:), DispatchQueue.main.async; none alone proves a background thread. |
| State/event model | Source-visible mechanisms: SwiftUI state property wrapper, ObservableObject, @Published. |
| Key frameworks/packages | SwiftUI, Metal, Foundation, MetalKit, AVFoundation; these are source dependencies, not architecture labels. |
Project structure
Source bundle/
└── LiDARDepth/
├── LiDARDepthApp.swift
├── ContentView.swift
├── Model/
│ ├── CameraController.swift
│ └── CameraManager.swift
└── Views/
├── Metal/
│ ├── MetalContentView.swift
│ ├── MetalTextureView.swift
│ ├── MetalPointCloud.swift
│ ├── MetalTextureColorThresholdDepth.swift
│ ├── MetalTextureColorZap.swift
│ ├── MetalTextureViewColor.swift
│ └── MetalTextureViewDepth.swift
└── DepthOverlay.swift
Structure observations
- Architecturally prominent files are ranked from entry points and role-named declarations; resource-only paths are omitted.
- Primary languages: Metal, Swift.
- The verified tree contains 3 project/configuration file(s) and 26 source declaration(s).
Overall architecture
flowchart LR
N1["LiDARDepthApp"]
N2["ContentView"]
N3["MTKColorTextureCoordinator"]
N4["AVFoundation APIs"]
N1 --> N2
N2 --> N3
N3 --> N4
Reference code
LiDARDepth/LiDARDepthApp.swift:10 — architecture anchor
@main
struct LiDARDepthApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}Interpretation
The arrows summarize the source-visible entry, role-named types or folders, and framework direction; when nodes come from structural folders, the sequence is a high-level interpretation rather than proof that every adjacent node calls the next. Ownership is claimed only where the next section cites a stored property or assignment. The diagram is intentionally limited to the dominant path into AVFoundation.
Ownership and state
classDiagram
ContentView *-- CameraManager : manager
ContentView *-- Float : maxDepth
ContentView *-- Float : minDepth
ContentView *-- Float : scaleMovement
Ownership evidence
LiDARDepth/ContentView.swift:14 — stored dependency or nearest verified ownership anchor
struct ContentView: View {
// ...
@StateObject private var manager = CameraManager()
// ...
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
ContentView |
CameraManager (manager) |
owns wrapper-managed state | Owning lexical scope |
ContentView |
Float (maxDepth) |
owns wrapper-managed state | Owning lexical scope |
ContentView |
Float (minDepth) |
owns wrapper-managed state | Owning lexical scope |
ContentView |
Float (scaleMovement) |
owns wrapper-managed state | Owning lexical scope |
Composition arrows indicate a source-visible construction expression or locally owned value state; aggregation means the owner stores or receives a dependency without proving exclusive lifetime ownership.
Concurrency, scheduling, and thread safety
Evidence limit: actor isolation, async/await, or Task creation does not by itself prove background-thread execution; Sendable conformance alone does not prove thread-safe mutation.
| Concern | Source mechanism | Verified placement or handoff | Evidence |
|---|---|---|---|
| Queue scheduling | DispatchQueue(label:) |
The source constructs a dispatch queue; its label alone does not prove a thread. | LiDARDepth/Model/CameraController.swift:26 |
| Queue scheduling | DispatchQueue.main.async |
The source addresses the main dispatch queue. | LiDARDepth/Model/CameraManager.swift:68 |
@MainActor/MainActor.run, DispatchQueue.main, and RunLoop.main are reported as distinct isolation, queue, and event-loop mechanisms. A plain Task is kept separate from Task.detached; neither is labeled as a background thread.
Reference code
LiDARDepth/Model/CameraController.swift:26 — representative execution boundary
class CameraController: NSObject, ObservableObject {
// ...
private let videoQueue = DispatchQueue(label: "com.example.apple-samplecode.VideoQueue", qos: .userInteractive)
// ...
}State propagation, frameworks, and dependencies
Evidence limit: an import proves a source-level compilation dependency at the cited line; it does not prove runtime use, architectural adoption, or whether a Swift package is a direct application dependency.
| Category | Mechanism or module | Verified role | Evidence |
|---|---|---|---|
| State propagation | SwiftUI state property wrapper |
A SwiftUI property wrapper supplies or observes UI state. | LiDARDepth/ContentView.swift:14 |
| State propagation | ObservableObject |
ObservableObject supplies an observation contract. | LiDARDepth/Model/CameraController.swift:17 |
| State propagation | @Published |
A published property can emit owner-controlled changes. | LiDARDepth/Model/CameraManager.swift:17 |
| Source import | SwiftUI |
The cited file imports this module; runtime use and architectural role are not inferred. | LiDARDepth/ContentView.swift:8 |
| Source import | Metal |
The cited file imports this module; runtime use and architectural role are not inferred. | LiDARDepth/ContentView.swift:10 |
| Source import | Foundation |
The cited file imports this module; runtime use and architectural role are not inferred. | LiDARDepth/Model/CVPixelBuffer+Extension.swift:8 |
| Source import | MetalKit |
The cited file imports this module; runtime use and architectural role are not inferred. | LiDARDepth/ContentView.swift:9 |
| Source import | AVFoundation |
The cited file imports this module; runtime use and architectural role are not inferred. | LiDARDepth/Model/CVPixelBuffer+Extension.swift:9 |
receive(on:) describes downstream delivery scheduling, while subscribe(on:) describes upstream subscription/request/cancel scheduling. An import Combine alone establishes neither behavior nor a Store, reducer, Redux, or other application architecture.
Class and protocol design
LiDARDepth/Model/CameraController.swift:12 — representative type boundary
protocol CaptureDataReceiver: AnyObject {
func onNewData(capturedData: CameraCapturedData)
func onNewPhotoData(capturedData: CameraCapturedData)
}| Type | Responsibility | Depends on or conforms to |
|---|---|---|
LiDARDepthApp |
Application entry and top-level composition | App |
ContentView |
User-interface presentation and input forwarding | View |
SliderDepthBoundaryView |
User-interface presentation and input forwarding | View |
CameraController |
View lifecycle, callbacks, and feature coordination | NSObject, ObservableObject |
CameraManager |
Long-lived feature or framework coordination | ObservableObject, CaptureDataReceiver |
MTKCoordinator |
Cross-object flow or session coordination | Concrete collaborators/imported frameworks |
MTKTextureCoordinator |
Cross-object flow or session coordination | MTKCoordinator |
MetalTextureView |
User-interface presentation and input forwarding | MetalRepresentable |
MetalPointCloudView |
User-interface presentation and input forwarding | UIViewRepresentable, MetalRepresentable |
MTKPointCloudCoordinator |
Cross-object flow or session coordination | MTKCoordinator |
The source explicitly defines local protocol relationships: CameraManager → CaptureDataReceiver, MetalPointCloudView → MetalRepresentable, MetalTextureColorThresholdDepthView → MetalRepresentable, MetalTextureColorZapView → MetalRepresentable, MetalTextureView → MetalRepresentable, MetalTextureViewColor → MetalRepresentable.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
manager (LiDARDepth/ContentView.swift:14) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
maxDepth (LiDARDepth/ContentView.swift:16) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
minDepth (LiDARDepth/ContentView.swift:17) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
scaleMovement (LiDARDepth/ContentView.swift:18) |
private |
Use is restricted to the lexical declaration and same-file extensions allowed by Swift. | Inference: keep state mutation or dependency lifetime inside the owning implementation. |
Reference code
LiDARDepth/ContentView.swift:14 — representative boundary
struct ContentView: View {
// ...
@StateObject private var manager = CameraManager()
// ...
}Swift declarations without a modifier are internal; explicit private, fileprivate, private(set), public, or open entries above are interpreted by language semantics. Objective-C/C samples instead rely on header and implementation boundaries, which are not equivalent to Swift lexical privacy.
Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application entry and top-level composition | LiDARDepthApp |
The source’s App suffix makes this role explicit. |
| View lifecycle, callbacks, and feature coordination | CameraController |
The source’s Controller suffix makes this role explicit. |
| Cross-object flow or session coordination | MTKColorTextureCoordinator, MTKColorThresholdDepthTextureCoordinator, MTKColorZapCoordinator, MTKCoordinator |
The source’s Coordinator suffix makes this role explicit. |
| Long-lived feature or framework coordination | CameraManager |
The source’s Manager suffix makes this role explicit. |
| User-interface presentation and input forwarding | ContentView, MetalPointCloudView, MetalTextureColorThresholdDepthView, MetalTextureColorZapView |
The source’s View suffix makes this role explicit. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| View-controller organization | LiDARDepth/Model/CameraController.swift:17 |
A controller is the verified coordination boundary; this is MVC-style only where a separate model is present. |
| Protocol-oriented abstraction | LiDARDepth/Model/CameraManager.swift:14 |
A local protocol and concrete conformance create an explicit capability boundary. |
| Delegate or data-source callbacks | LiDARDepth/Model/CameraController.swift:157 |
Callback protocols invert event delivery back into the sample’s owner. |
| Coordinator | LiDARDepth/Views/Metal/MetalTextureViewColor.swift:22 |
A role-named coordinator centralizes cross-object flow. |
| Binding-based state propagation | LiDARDepth/ContentView.swift:86 |
A binding exposes controlled read/write access while the upstream owner remains authoritative. |
Main application flow
sequenceDiagram
participant Session as Capture session
participant Sync as Data synchronizer
participant Controller as CameraController
participant Manager as CameraManager
participant View as Metal views
Session->>Sync: video + depth outputs
Sync->>Controller: synchronized collection
Controller->>Controller: create textures and CameraCapturedData
Controller->>Manager: onNewData(capturedData)
Manager->>Manager: update stable capturedData reference
Manager-->>View: published dataAvailable/state
View->>View: coordinators render textures
Reference code
LiDARDepth/Model/CameraController.swift:159 — dataOutputSynchronizer
func dataOutputSynchronizer(_ synchronizer: AVCaptureDataOutputSynchronizer,
didOutput synchronizedDataCollection: AVCaptureSynchronizedDataCollection) {
// Retrieve the synchronized depth and sample buffer container objects.
guard let syncedDepthData = synchronizedDataCollection.synchronizedData(for: depthDataOutput) as? AVCaptureSynchronizedDepthData,
let syncedVideoData = synchronizedDataCollection.synchronizedData(for: videoDataOutput) as? AVCaptureSynchronizedSampleBufferData else { return }
guard let pixelBuffer = syncedVideoData.sampleBuffer.imageBuffer,
let cameraCalibrationData = syncedDepthData.depthData.cameraCalibrationData else { return }
// Package the captured data.
let data = CameraCapturedData(depth: syncedDepthData.depthData.depthDataMap.texture(withFormat: .r16Float, planeIndex: 0, addToCache: textureCache),
colorY: pixelBuffer.texture(withFormat: .r8Unorm, planeIndex: 0, addToCache: textureCache),
colorCbCr: pixelBuffer.texture(withFormat: .rg8Unorm, planeIndex: 1, addToCache: textureCache),
cameraIntrinsics: cameraCalibrationData.intrinsicMatrix,
cameraReferenceDimensions: cameraCalibrationData.intrinsicMatrixReferenceDimensions)
delegate?.onNewData(capturedData: data)
}Naming conventions
- Types: App: LiDARDepthApp; Controller: CameraController; Coordinator: MTKColorTextureCoordinator, MTKColorThresholdDepthTextureCoordinator, MTKColorZapCoordinator, MTKCoordinator, MTKDepthTextureCoordinator; Manager: CameraManager; View: ContentView, MetalPointCloudView, MetalTextureColorThresholdDepthView, MetalTextureColorZapView, MetalTextureDepthView.
- Protocols:
CaptureDataReceiver,MetalRepresentable. - Methods:
onNewData,onNewPhotoData,setupSession,setupCaptureInput,setupCaptureOutputs,startStream,stopStream,dataOutputSynchronizer. - Files:
LiDARDepth/LiDARDepthApp.swift,LiDARDepth/ContentView.swift,LiDARDepth/Model/CameraController.swift,LiDARDepth/Model/CameraManager.swift,LiDARDepth/Views/Metal/MetalTextureView.swift,LiDARDepth/Views/Metal/MetalTextureViewColor.swift.
Architecture takeaways
LiDARDepthAppis the main source-visible entry or composition anchor for this sample.- Framework work reaches SwiftUI, Metal, MetalKit, AVFoundation through a deliberately small high-level chain; the detailed API graph remains inside the cited implementation files.
- Stored-property evidence identifies lifecycle collaboration; it does not by itself prove exclusive object ownership.
- Access-control conclusions separate verified language visibility from the likely design rationale.
- Local protocol relationships provide an explicit substitution boundary.
Source map
| Source file | Relevant symbols |
|---|---|
LiDARDepth/LiDARDepthApp.swift |
Cited implementation, LiDARDepthApp |
LiDARDepth/ContentView.swift |
Cited implementation, SwiftUI state property wrapper, SwiftUI, Metal, MetalKit, ContentView, SliderDepthBoundaryView, ContentView_Previews |
LiDARDepth/Model/CameraController.swift |
CaptureDataReceiver, CameraController, Cited implementation, DispatchQueue(label:), ObservableObject, ConfigurationError |
LiDARDepth/Model/CameraManager.swift |
Cited implementation, DispatchQueue.main.async, @Published, CameraManager, CameraCapturedData |
LiDARDepth/Views/Metal/MetalTextureViewColor.swift |
MTKColorTextureCoordinator, MetalTextureViewColor |
LiDARDepth/Model/CVPixelBuffer+Extension.swift |
Foundation, AVFoundation, Feature implementation |
LiDARDepth/Views/Metal/MetalContentView.swift |
MetalRepresentable, MTKCoordinator |
LiDARDepth/Views/Metal/MetalTextureView.swift |
MTKTextureCoordinator, MetalTextureView |
LiDARDepth/Views/Metal/MetalPointCloud.swift |
MetalPointCloudView, MTKPointCloudCoordinator, CameraModes |
LiDARDepth/Views/Metal/MetalTextureColorThresholdDepth.swift |
MetalTextureColorThresholdDepthView, MTKColorThresholdDepthTextureCoordinator |
LiDARDepth/Views/Metal/MetalTextureColorZap.swift |
MetalTextureColorZapView, MTKColorZapCoordinator |
LiDARDepth/Views/Metal/MetalTextureViewDepth.swift |
MetalTextureDepthView, MTKDepthTextureCoordinator |
LiDARDepth/Views/DepthOverlay.swift |
DepthOverlay |
LiDARDepth/Views/Metal/MetalEnvironment.swift |
MetalEnvironment |
LiDARDepth/Views/ZoomOnTap.swift |
ZoomOnTap |
LiDARDepth/Views/Metal/Shaders/shaders.metal |
Feature implementation |