Sample CodeiOS, iPadOS, Mac CatalystReviewed 2026-07-21View on Apple Developer

Enhancing live video by leveraging TrueDepth camera data

At a glance

Item Summary
Purpose Synchronize color, depth, and face metadata; derive a foreground mask; blend a chosen background; and render with Metal.
App architecture Storyboard UIKit controller owns the end-to-end capture and Core Image pipeline; a custom MTKView owns display rendering.
Main patterns MVC, AVFoundation delegate, synchronized stream aggregation, queue confinement, and renderer encapsulation.
Project style One screen controller, one reusable preview view, and a small Metal shader folder.

Project structure

TrueDepthBackdrop/
├── CameraViewController.swift  # UI, session, synchronization, image processing
├── PreviewMetalView.swift      # Thread-safe image handoff and Metal render loop
├── Shaders/
│   └── PassThrough.metal       # Vertex and fragment functions
├── Base.lproj/Main.storyboard  # View hierarchy and outlets
└── AppDelegate.swift

Structure observations

  • CameraViewController.swift is deliberately broad: it is the feature controller, capture owner, delegate, and image-processing coordinator.
  • GPU presentation is separated into PreviewMetalView, which hides Metal objects and draw-loop state.
  • There is no view model, service protocol, or separate domain layer.

Overall architecture

Reference code

TrueDepthBackdrop/CameraViewController.swift:407 and TrueDepthBackdrop/CameraViewController.swift:622 — synchronized callback boundary

outputSynchronizer = AVCaptureDataOutputSynchronizer(
    dataOutputs: [videoDataOutput, depthDataOutput, metadataOutput]
)
outputSynchronizer!.setDelegate(self, queue: dataOutputQueue)

func dataOutputSynchronizer(
    _ synchronizer: AVCaptureDataOutputSynchronizer,
    didOutput synchronizedDataCollection: AVCaptureSynchronizedDataCollection
) { /* build the mask and composited image */ }

Interpretation

The view controller is the application-level owner and receives one synchronized collection per processing step. It transforms the depth buffer into a matte, blends the color frame with the selected background, then hands a CIImage to the specialized renderer.

Ownership and state

Ownership evidence

TrueDepthBackdrop/CameraViewController.swift:18 — controller-owned graph and storyboard-owned view

@IBOutlet weak private var previewView: PreviewMetalView!
private let session = AVCaptureSession()
private let sessionQueue = DispatchQueue(label: "session queue")
private let dataOutputQueue = DispatchQueue(label: "video data queue")
private let videoDataOutput = AVCaptureVideoDataOutput()
private let depthDataOutput = AVCaptureDepthDataOutput()
private let metadataOutput = AVCaptureMetadataOutput()
Owner Object or state Relationship Mutation authority
Storyboard/view hierarchy Controller and PreviewMetalView Instantiates and retains UI UIKit lifecycle
CameraViewController Session, inputs, outputs, synchronizer, device Creates and retains Controller, with session mutations on sessionQueue
CameraViewController Background image and mask tuning values Retains Controller callbacks and image-picker flow
PreviewMetalView Internal image snapshot, transforms, Metal resources Receives image; creates renderer state View, synchronized through syncQueue

Class and protocol design

TrueDepthBackdrop/CameraViewController.swift:14 — framework callback composition

class CameraViewController: UIViewController,
    AVCaptureDataOutputSynchronizerDelegate,
    UIImagePickerControllerDelegate,
    UINavigationControllerDelegate {
    // ...
}
Type Responsibility Depends on
CameraViewController Capture lifecycle, authorization, interruptions, synchronized processing, background selection UIKit, AVFoundation, Core Image
PreviewMetalView Accept a CIImage, snapshot thread-shared state, and render a textured quad MetalKit, Core Image
AVCaptureDataOutputSynchronizerDelegate Deliver aligned video/depth/metadata collections AVFoundation protocol
Image-picker/navigation delegates Deliver a replacement background UIKit protocols

This is protocol-oriented at framework callback boundaries, not through app-defined capabilities.

Access control

Symbol Access Verified effect Likely rationale
Controller outlets private weak Only the controller accesses them; UIKit view hierarchy retains the views. Accurately expresses storyboard ownership and avoids a second strong owner.
SessionSetupResult and setup state private Configuration outcomes cannot become an app-wide state contract. Keeps recovery policy local to the screen.
Session, devices, outputs, queues, synchronizer private No collaborator can mutate the capture graph directly. Protects thread and configuration invariants.
Preview’s internal image/mirroring/rotation and Metal objects private Callers set only image, mirroring, and rotation. Separates a small input surface from render-loop state.
Controller delegate methods internal by default Framework/runtime can invoke conforming witnesses; app module can also see them. Required protocol and Objective-C runtime integration.

Reference code

TrueDepthBackdrop/PreviewMetalView.swift:21 — narrow input with private synchronized copy

var image: CIImage? {
    didSet {
        syncQueue.sync { internalImage = image }
    }
}
private var internalImage: CIImage?
private let syncQueue = DispatchQueue(label: "Preview View Sync Queue")

Logic ownership and placement

Logic Owning type or file Placement rationale
Authorization, start/stop, interruption recovery CameraViewController Screen and session lifecycle are coupled
Capture graph construction and depth-format selection configureSession() One private mutation boundary for the session
Align video, depth, and face data synchronizer delegate method Framework delivers all inputs together
Depth cutoff, blur/gamma matte, background blend synchronizer delegate method Per-frame pipeline operates on aligned buffers
Image-to-texture rendering and transforms PreviewMetalView Renderer-specific, independent of capture configuration

Design patterns

Pattern Source evidence Purpose or tradeoff
MVC/controller TrueDepthBackdrop/CameraViewController.swift:14 One controller coordinates the screen and feature; simple but large.
Delegate TrueDepthBackdrop/CameraViewController.swift:622 Receives aligned output without polling.
Stream aggregator TrueDepthBackdrop/CameraViewController.swift:407 Synchronizer combines three capture outputs into one processing unit.
Thread confinement TrueDepthBackdrop/CameraViewController.swift:36, TrueDepthBackdrop/CameraViewController.swift:43 Separates blocking session work from real-time data processing and UI.
Encapsulated renderer TrueDepthBackdrop/PreviewMetalView.swift:12, TrueDepthBackdrop/PreviewMetalView.swift:267 Hides Metal setup and draw mechanics behind image/rotation inputs.

Naming conventions

  • Types name framework role: CameraViewController and PreviewMetalView.
  • Capture properties name the exact framework object: videoDataOutput, depthDataOutput, outputSynchronizer.
  • State Booleans use is/past-participle forms: isSessionRunning, renderingEnabled.
  • Methods use lifecycle/action verbs: configureSession, sessionWasInterrupted, chooseBackground, configureMetal.

Architecture takeaways

  • Make one queue-confined owner responsible for an AVCaptureSession graph.
  • Synchronize heterogeneous outputs before applying per-frame business logic.
  • Keep the Metal draw loop behind a small renderer input surface.
  • A large controller is understandable for a focused sample, but production growth would justify extracting capture and image-processing services.

Source map

Source file Relevant symbols
TrueDepthBackdrop/CameraViewController.swift capture graph, lifecycle, synchronizer delegate, Core Image blend
TrueDepthBackdrop/PreviewMetalView.swift renderer inputs, transforms, Metal lifecycle
TrueDepthBackdrop/Shaders/PassThrough.metal vertexPassThrough, fragmentPassThrough
TrueDepthBackdrop/AppDelegate.swift application entry delegate