WWDC.ai

Build a responsive camera app that launches quickly

Optimize AVFoundation camera startup by deferring non-preview outputs, rendering preview efficiently, adapting to pressure, and using AVProVideoStorage.

Watch on Apple Developer

TL;DR

  • Make the first preview frame the primary launch metric: create only launch-critical UI up front, configure AVCaptureSession off the main thread, commit once, and never call startRunning/stopRunning on the main thread.
  • Use deferred start in iOS 26 and later to initialize only the preview path before the first frame; defer photo/movie outputs and use automatic or manual runDeferredStartWhenNeeded depending on your preview pipeline.
  • Pair deferred AVCapturePhotoOutput with responsive capture so the app can accept a photo capture before deferred photo initialization has fully completed.
  • Maintain runtime quality by choosing AVCaptureVideoPreviewLayer when possible, monitoring hardwareCost and AVCaptureDevice.systemPressureState, and using AVProVideoStorage for deterministic high-data-rate video writes.

Launch around the first preview frame

The session frames camera launch performance around how quickly the first preview frame appears. A camera app can feel ready before every control, picker, or background resource is initialized, so launch work should be split into preview-critical and post-preview phases.

For an AVCam-style interface, the preview and shutter button are launch-critical. Elements such as the image well, mode picker, preferences, and other non-essential resources should be created or faded in after preview is already streaming.

  • Create AVCaptureSession as soon as the main thread finishes minimal UI setup, but create/configure it off the main thread so UI scene creation can proceed in parallel.
  • Commit a single AVCaptureSession configuration during launch; avoid multiple reconfiguration passes before preview.
  • Treat startRunning and stopRunning as blocking calls and keep them off the main thread.
  • Only initialize the output needed to render preview before the first frame; other capture outputs can be deferred.

Use deferred start for non-preview outputs

Deferred start, available in iOS 26 and later, lets an AVCaptureSession postpone initialization of outputs that are not needed to show preview. The preview path starts first, and deferred outputs are initialized later either automatically or when the app explicitly signals readiness.

Every AVCaptureOutput and AVCaptureVideoPreviewLayer has an isDeferredStartEnabled property. For fastest launch, disable deferral only for the output that renders preview, and enable it for outputs such as AVCapturePhotoOutput or movie recording outputs.

  • Apps recompiled with the iOS 26 or later SDK use automatic deferred start by default through AVCaptureSession.automaticallyRunsDeferredStart.
  • Automatic mode chooses a time shortly after preview appears and reports progress through AVCaptureSessionDeferredStartDelegate callbacks.
  • Manual mode sets automaticallyRunsDeferredStart to false and calls runDeferredStartWhenNeeded after the app has presented the first frame or finished other startup work.
  • Manual mode is especially useful when rendering preview with AVCaptureVideoDataOutput instead of AVCaptureVideoPreviewLayer.

Automatic deferred start with a preview layer and deferred photo output

Only the preview layer is required for launch; photo output initialization is deferred until after preview begins.

import AVFoundation

final class DeferredStartDelegate: NSObject, AVCaptureSessionDeferredStartDelegate {
    func sessionWillRunDeferredStart(_ session: AVCaptureSession) {
        // Prepare background resources before deferred outputs initialize.
    }

    func sessionDidRunDeferredStart(_ session: AVCaptureSession) {
        // Deferred outputs are initialized and ready.
    }
}

let captureSession = AVCaptureSession()
captureSession.beginConfiguration()
captureSession.automaticallyRunsDeferredStart = true

let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.isDeferredStartEnabled = false

let photoOutput = AVCapturePhotoOutput()
photoOutput.isDeferredStartEnabled = true
captureSession.addOutput(photoOutput)

captureSession.setDeferredStartDelegate(delegate,
    deferredStartDelegateCallbackQueue: sessionQueue)
captureSession.commitConfiguration()
sessionQueue.async { captureSession.startRunning() }

Manual deferred start after first frame presentation

When the app owns preview rendering, wait until the first displayed frame before starting deferred output initialization.

import AVFoundation
import QuartzCore

captureSession.automaticallyRunsDeferredStart = false
videoOutput.isDeferredStartEnabled = false
photoOutput.isDeferredStartEnabled = true

private var firstFramePresented = false

if let drawable = layer.nextDrawable(), !firstFramePresented {
    drawable.addPresentedHandler { _ in
        // Create postponed UI, then allow deferred outputs to initialize.
        captureSession.runDeferredStartWhenNeeded()
    }
    firstFramePresented = true
}

Keep capture responsive after deferring photo output

Deferring AVCapturePhotoOutput improves time to first preview frame, but it does not automatically reduce time to first completed photo capture. If the user taps the shutter before the photo output has finished initializing, the output must still finish deferred startup.

Responsive capture addresses this by adding buffering between starting a capture and processing. When supported, enable it alongside deferred photo output so a user can launch, see preview quickly, and still capture the moment.

  • Set AVCapturePhotoOutput.maxPhotoQualityPrioritization as appropriate for your app's quality goals.
  • Enable isResponsiveCaptureEnabled only when isResponsiveCaptureSupported is true.
  • Use this combination for camera apps that need both fast preview launch and immediate shutter responsiveness.

Enable responsive capture on photo output

Responsive capture lets the photo output accept captures immediately when the feature is supported.

import AVFoundation

func configurePhotoOutput(for session: AVCaptureSession, device: AVCaptureDevice) {
    let photoOutput = AVCapturePhotoOutput()
    guard session.canAddOutput(photoOutput) else { return }

    session.addOutput(photoOutput)
    photoOutput.maxPhotoQualityPrioritization = .quality
    photoOutput.isResponsiveCaptureEnabled = photoOutput.isResponsiveCaptureSupported
}

Render preview with the right output

AVCaptureVideoPreviewLayer is the preferred preview renderer when the app only needs to show the camera feed. It is optimized for low latency, low CPU/GPU overhead, HDR tone mapping, and power-efficient preview rendering without app-side per-frame processing.

Use AVCaptureVideoDataOutput when the app needs per-frame access, custom processing, Metal integration, analysis, or per-frame overlays. This flexibility comes with more responsibility: keep per-frame work short to avoid dropped frames, and adopt manual deferred start to recover launch gains.

  • Choose AVCaptureVideoPreviewLayer for simple, efficient preview and automatic deferred-start behavior when built with the iOS 26 or later SDK.
  • Choose AVCaptureVideoDataOutput when frame processing or custom rendering is required.
  • With AVCaptureVideoDataOutput, avoid expensive per-frame CPU, GPU, or Apple Neural Engine work during launch and while the device is under pressure.

Sustain performance under cost, pressure, and storage constraints

As capture configurations become more complex, the app should evaluate whether the hardware can support them and adapt when conditions change. AVCaptureSession.hardwareCost reports active hardware usage; values above 1 indicate an unsupported configuration. AVCaptureDevice.systemPressureState reports pressure changes that may require reducing workload.

High data-rate video capture, such as ProRes, also depends on consistent file I/O. AVProVideoStorage, new in iOS 27, manages system-wide pre-allocated storage so supported movie recording and AVAssetWriter workflows can get deterministic write performance.

  • Hardware cost is affected by the number of cameras, active formats such as 1080p or 4K, maximum frame rates, frame-rate overrides, and binned formats.
  • After committing the session configuration, check hardwareCost and reconfigure if it exceeds 1.0 before starting the session.
  • Observe AVCaptureDevice.systemPressureState and respond by reducing frame rate, throttling GPU or Apple Neural Engine work, or minimizing UI updates.
  • For ProRes or other high-bandwidth recording, check AVProVideoStorage support and remaining capacity before starting capture.

Check hardware cost and observe system pressure

Validate the configuration before running, then adapt when system pressure changes.

import AVFoundation

captureSession.beginConfiguration()
// Configure inputs, outputs, formats, and connections.
captureSession.commitConfiguration()

guard captureSession.hardwareCost <= 1.0 else {
    print("hardwareCost \(captureSession.hardwareCost) - cannot start session. Reconfiguring.")
    setupLowCostConfiguration()
    return
}

captureSession.startRunning()

let observer = activeVideoInput?.device.observe(\.systemPressureState,
    options: [.initial, .new]) { device, change in
        // Reduce frame rate, throttle processing, or simplify UI.
    }

Use AVProVideoStorage for deterministic high-data-rate recording

When supported and not busy, AVProVideoStorage writes to pre-allocated storage during capture and moves the recording to the requested URL when finished.

import AVFoundation

guard AVProVideoStorage.isSupported,
      let pvs = AVProVideoStorage.shared else { return }

guard pvs.remainingCapacity != 0 else {
    pvs.openSettings()
    return
}

let movieOutput = AVCaptureMovieFileOutput()
guard movieOutput.isProVideoStorageSupported else { return }
guard !pvs.isBusy else { return }

let url = FileManager.default.temporaryDirectory
    .appendingPathComponent(UUID().uuidString)
    .appendingPathExtension("mov")

movieOutput.usesProVideoStorage = true
movieOutput.startRecording(to: url, recordingDelegate: delegate)

Resources

Unofficial, not associated with Apple. BySuperwall

On this page

Ask AI