iPhone Duo has two front cameras: an outer ultrawide capable of up to 4K at 120fps, and an under-display inner ultrawide up to 1080p at 60fps. Both have square sensors with an ultrawide field of view.
Decide first: virtual or individual
Virtual front camera — the system switches between inner and outer automatically as the device opens and closes. This is the simplest correct answer for most apps.
AVCaptureDeviceDiscoverySession(
deviceTypes: [.builtInWideCamera, .builtInUltraWideCamera],
mediaType: .video,
position: .front
)
Individual cameras — for direct control:
.builtInOuterUltraWideCamera
.builtInInnerUltraWideCamera
.builtInDualWideCamera
If you choose individual cameras, you take on responsibility for switching when the device opens or closes, and you need the direction coordinator.
The direction coordinator
directionCoordinator = AVCaptureDeviceDirectionCoordinator(
view: view,
deviceTypes: [
.builtInOuterUltraWideCamera,
.builtInInnerUltraWideCamera,
.builtInDualWideCamera,
],
changeHandler: { [weak self] map in
self?.updateCameraSession(map)
}
)
It lives in AVKit and is main-actor isolated. It hands you an AVCaptureDeviceDescriptor — sendable and main-actor-safe — rather than an AVCaptureDevice directly, so pass the descriptor to your camera actor for thread-safe reconfiguration. Capturing the device itself across actor boundaries is the mistake this API exists to prevent.
Multiple displays
For a session on each display, create a separate AVCaptureSession and direction coordinator per display, and use the scene accessories API to manage the views.
How to verify
Start capture, then fold and unfold the device repeatedly while watching the preview. It should follow the active display without dropping frames or stalling, and the correct lens should be selected in each state.