Sample CodeiOS 26.0, iPadOS 26.0, Mac Catalyst 26.0, Xcode 26.0Reviewed 2026-07-21View on Apple Developer

Capturing Cinematic video

At a glance

Item Summary
Purpose Record Cinematic video while visualizing detected focus objects and letting the user change tracking/fixed focus and simulated aperture.
App architecture SwiftUI MVVM-style app: a Camera protocol feeds generic views, CameraModel owns presentation state, and CaptureService owns AVFoundation work on a serial actor executor.
Main patterns Protocol substitution, actor service, output-service composition, observable metadata store, delegate-to-async recording.
Project style Main model/service files plus Capture/, Model/, Views/, Support/, and preview-only model content.

Project structure

CinematicCaptureSample/
├── CinematicCaptureSampleApp.swift
├── CameraModel.swift
├── CameraView.swift
├── CaptureService.swift
├── Capture/
│   ├── DeviceLookup.swift
│   ├── MovieCapture.swift
│   └── SPCObserver.swift
├── Model/
│   ├── Camera.swift
│   ├── DataTypes.swift
│   └── MediaLibrary.swift
├── Views/
│   ├── CameraPreview.swift
│   ├── FocusOverlayView.swift
│   ├── Controls/SimulatedApertureView.swift
│   ├── Overlays/
│   └── Toolbars/
├── Support/
└── Preview Content/PreviewCameraModel.swift

Structure observations

  • The sample follows the same high-level separation as AVCam but removes photo capture and specializes the service/view layer for Cinematic metadata and focus controls.
  • CaptureService.swift includes the capture actor and its small observable CinematicMetadataManager, keeping metadata production beside the AVFoundation delegates.
  • Views depend on the Camera protocol, while preview content provides a hardware-free conformer.

Overall architecture

Reference code

CinematicCaptureSample/CinematicCaptureSampleApp.swift:13 — composition root

@State private var camera = CameraModel()

WindowGroup {
    CameraView(camera: camera)
        .task {
            await camera.start()
        }
}

Interpretation

The app constructs one long-lived model. The model presents a main-actor interface to SwiftUI, while the capture actor owns session configuration, video/metadata callbacks, and movie capture. A separate actor saves the completed movie. Calling this MVVM is an interpretation supported by the explicit model mediation and source comments.

Ownership and state

Ownership evidence

CinematicCaptureSample/CameraModel.swift:58 — model composition

private let mediaLibrary = MediaLibrary()
private let captureService: CaptureService

init() {
    captureService = CaptureService(
        previewLayer: preview as! AVSampleBufferDisplayLayer
    )
}
Owner Object or state Relationship Mutation authority
App CameraModel Creates and retains with SwiftUI @State Model mutates observable UI state on MainActor.
CameraModel Preview layer, capture service, media library Creates all three and injects preview into service Model coordinates; actors own service internals.
CaptureService Session, movie output service, metadata output/cache Creates and retains capture graph Capture actor on sessionQueue.
CaptureService CinematicMetadataManager Creates and updates its metadata array Source updates come from capture actor; the property setter is technically module-internal.
MovieCapture Movie output/delegate/timer Owns recording lifecycle MovieCapture.

Class and protocol design

Reference code

CinematicCaptureSample/Model/Camera.swift:13 — view-facing capability

@MainActor
protocol Camera: AnyObject, SendableMetatype {
    var status: CameraStatus { get }
    var metadataManager: CinematicMetadataManager { get }
    var preview: CALayer { get }
    func toggleRecording() async
    func tapPreview(at point: CGPoint) async
}
Type Responsibility Depends on
Camera Define view-facing state and Cinematic focus commands AVFoundation/Core Animation data types
CameraModel Mediate views, capture, save, and observable state CaptureService, MediaLibrary
CaptureService Configure Cinematic capture, deliver preview/metadata, and control focus AVFoundation, MovieCapture
CinematicMetadataManager Publish normalized focus metadata to SwiftUI Observation, CinematicFocusMetadata
MovieCapture Adapt file-output recording and callback completion AVCaptureMovieFileOutput

Access control

Symbol Access Verified effect Likely rationale
Model status, activity, switching state, thumbnails, errors, aperture limits private(set) Views can observe but cannot assign Keeps CameraModel as UI-state mutation boundary.
Model service/library references private let Views interact only through model commands Hides collaborator lifecycle and concrete types.
Capture session, movie service, inputs, metadata cache, and setup helpers private Capture graph is inaccessible outside CaptureService Preserves actor-confined session invariants.
CGPoint.normalized(for:) extension fileprivate Usable throughout FocusOverlayView.swift, nowhere else A same-file geometry helper supports the view without becoming a module utility.
App protocols/types implicit internal Shared in the app module only The target is not a public framework.

Reference code

CinematicCaptureSample/Views/FocusOverlayView.swift:110 — same-file-only helper

fileprivate extension CGPoint {
    func normalized(for proxy: GeometryProxy) -> CGPoint {
        .init(x: x / proxy.size.width, y: y / proxy.size.height)
    }
}

No public or open declaration appears in the Swift source.

Logic ownership and placement

Logic Owning type or file Placement rationale
UI state and user-intent translation CameraModel Main-actor observable boundary.
Session/device/Cinematic configuration CaptureService Actor-confined AVFoundation ownership.
Metadata conversion to normalized focus records Capture-service metadata delegate extension Runs where raw metadata output and coordinate conversion are available.
Focus overlay geometry and gestures FocusOverlayView View-specific normalization/rendering stays in SwiftUI.
Recording lifecycle MovieCapture Contains file-output delegate and timer state.
Photos save and thumbnail stream MediaLibrary Separate PhotoKit actor boundary.

Design patterns

Pattern Source evidence Purpose or tradeoff
MVVM-style mediation CinematicCaptureSample/CameraModel.swift:13 and class comment Keeps SwiftUI free of session mutation.
Protocol-oriented substitution CinematicCaptureSample/Model/Camera.swift:13 Allows PreviewCameraModel in previews/Simulator.
Actor-isolated service CinematicCaptureSample/CaptureService.swift:18 and custom executor at line 70 Runs session work serially away from MainActor.
Observable store CinematicCaptureSample/CaptureService.swift:12 and CinematicMetadataManager Publishes focus metadata to overlay views without exposing raw outputs.
Delegate-to-async adapter CinematicCaptureSample/Capture/MovieCapture.swift:54 Converts recording completion into an async result.

Main application flow

Reference code

CinematicCaptureSample/CaptureService.swift:498 — metadata publication

nonisolated func metadataOutput(_ output: AVCaptureMetadataOutput,
                                didOutput metadataObjects: [AVMetadataObject],
                                from connection: AVCaptureConnection) {
    assumeIsolated { isolatedSelf in
        isolatedSelf.metadataObjectsCache = metadataObjects
        // Convert objects to CinematicFocusMetadata values.
        isolatedSelf.metadataManager.cinematicFocusMetadata = cinematicFocusMetadataObjects
    }
}

Naming conventions

  • Types: responsibility and feature names (CameraModel, CaptureService, CinematicMetadataManager, FocusOverlayView).
  • Protocols: capability nouns (Camera, OutputService).
  • Methods: user commands (toggleRecording, tapPreview, longPressPreview) and setup verbs (setupCinematicCapture, metadataVDOSetup).
  • State: is..., min..., and max... prefixes make Boolean/range semantics visible.
  • Files: primary type per file, folders by Capture/Model/Views/Support responsibility.

Architecture takeaways

  • Keep presentation state on the main actor and capture mutation on a dedicated serial actor executor.
  • Expose focus metadata as small normalized value objects instead of raw AVFoundation objects in views.
  • Use a protocol-backed camera model to keep hardware-free SwiftUI previews possible.
  • A same-file fileprivate extension is a precise home for view-only geometry helpers.
  • The movie-output delegate remains inside MovieCapture, while CaptureService coordinates Cinematic-specific behavior.

Source map

Source file Relevant symbols
CinematicCaptureSample/CinematicCaptureSampleApp.swift Composition root
CinematicCaptureSample/CameraModel.swift Main-actor UI model
CinematicCaptureSample/CaptureService.swift Capture actor, metadata store/delegates, Cinematic focus
CinematicCaptureSample/Model/Camera.swift Camera, CinematicFocusMetadata
CinematicCaptureSample/Capture/MovieCapture.swift Recording delegate/async adapter
CinematicCaptureSample/Model/MediaLibrary.swift PhotoKit save actor
CinematicCaptureSample/Views/FocusOverlayView.swift Focus UI and geometry helper