iPhone Duo Camera Apps: Virtual Front Camera and Direction Switching with AVFoundation
- Author
- Ehsan Azish · 3NSOFTS
- Updated
- Read time
- 12 min read
- Level
- Intermediate to Senior
- Platform
- AVFoundation capture basics, UIKit preview hosting, Xcode 27.1
Implementation Notes
- ~/ What broke: A production edge case that generic tutorials skip.
- ~/ What to do: Ship the production fix with clear state, errors, and fallback behavior.
Quick Answer
Keep the camera path you already use unless the product needs a capability that differs between iPhone Duo's two physical front cameras. A normal front-camera discovery session returns the Virtual Front Camera on Duo, and the system follows the app between the outer and inner displays. If capture is the product and you need the full resolution, frame-rate, or depth capabilities of a physical camera, discover those cameras directly and use AVCaptureDeviceDirectionCoordinator to determine which one faces the same direction as the preview.
Do not infer camera direction from .front or .back. On Duo, a camera's physical position stays constant while the display facing the user can change.
Requirements
- Xcode 27.1 and the iOS 27.1 SDK for Duo-specific camera APIs.
- A real
AVCaptureSessionarchitecture that keeps session mutation off the main actor. - A
UIViewassociated with the preview. The direction coordinator uses this view as its frame of reference. - Camera permission copy and a usable denied-permission state.
Choose the Smallest Camera Strategy That Meets the Product Need
There are two legitimate paths:
| Strategy | Use it when | Tradeoff | |---|---|---| | Virtual Front Camera | Video calls, profile photos, document capture, or an existing front-camera flow | The system switches displays for you, but exposes only capabilities common to both front cameras | | Physical cameras plus direction coordinator | Camera creation is a primary feature and the app needs higher resolution, higher frame rate, depth, or deliberate rear-camera selfies | Your app owns camera selection, session reconfiguration, mirroring, and transition behavior |
Apple states that the inner front camera supports up to 1080p at 60 fps, while the outer front camera supports up to 4K at 120 fps. The virtual device therefore tops out at the shared 1080p/60-fps capability, and depth requires direct access to a physical camera. Treat those limits as a product decision before adding coordination code.
Start with the Virtual Front Camera
Existing discovery code remains the right baseline. Ask for a front wide or ultrawide camera:
import AVFoundation
enum CameraLookup {
static let frontDiscovery = AVCaptureDevice.DiscoverySession(
deviceTypes: [
.builtInWideAngleCamera,
.builtInUltraWideCamera,
],
mediaType: .video,
position: .front
)
static var frontCamera: AVCaptureDevice? {
frontDiscovery.devices.first
}
static func activePhysicalCamera(
for camera: AVCaptureDevice
) -> AVCaptureDevice? {
camera.isVirtualDevice
? camera.activePrimaryConstituent
: camera
}
}
On a single-display iPhone this finds the expected front camera. On iPhone Duo it finds the virtual device, which streams from the camera above the display showing the app and switches as the device opens or closes. Keep one code path across iPhone models and query capabilities from the device you actually receive.
Do not use a model-name check to decide whether the virtual device exists. Discovery and capability checks are the stable contract.
Use Physical Cameras Only for a Concrete Capability
When the product needs direct access, discover the Duo cameras by their device types:
let duoDiscovery = AVCaptureDevice.DiscoverySession(
deviceTypes: [
.builtInOuterUltraWideCamera,
.builtInInnerUltraWideCamera,
.builtInDualWideCamera,
],
mediaType: .video,
position: .unspecified
)
Include every built-in type the app may capture from, including rear cameras. A rear camera can face the same direction as the app when an open Duo is turned around. External and Continuity cameras are outside the direction coordinator's built-in-camera mapping and need their existing handling.
Coordinate Direction Relative to the Preview
Create the coordinator on the main actor and retain it for as long as the preview view remains onscreen:
import AVKit
@MainActor
final class CameraDirectionObserver {
private var coordinator: AVCaptureDeviceDirectionCoordinator?
func start(
previewView: UIView,
onChange: @escaping (AVCaptureDeviceDirectionMap) -> Void
) {
coordinator = AVCaptureDeviceDirectionCoordinator(
view: previewView,
deviceTypes: [
.builtInOuterUltraWideCamera,
.builtInInnerUltraWideCamera,
.builtInDualWideCamera,
],
changeHandler: onChange
)
}
}
The handler first reports the current configuration, then reports changes as the device opens, closes, flips, or moves the app to another display. Read forwardFacingDeviceDescriptors and choose the descriptor that matches your product's priority. Pass that sendable descriptor to the camera actor; do not call AVFoundation session APIs directly from the main-actor callback.
The boundary should look like this:
- The main actor observes direction relative to the preview view.
- A pure selection rule chooses a descriptor.
- The camera actor resolves the descriptor and reconfigures the session.
- The main actor updates preview mirroring and visible controls.
For a two-display experience, create one coordinator per preview view. Direction is relative to each view, so the same camera may be forward-facing for one display and backward-facing for the other.
Preserve Continuity During a Camera Switch
Opening Duo must not look like a capture failure. Reconfigure the existing session rather than discarding the whole camera screen. Keep recording and capture state in an owner that survives the layout transition, and make the switch explicit if an active recording cannot continue across inputs.
Your transition policy should answer:
- Does an in-progress video continue, pause, or finish before switching?
- Does zoom map to an equivalent field of view or reset visibly?
- Does the preview need mirroring when a rear camera becomes forward-facing?
- Which controls remain available when the new camera has fewer capabilities?
- What does the user see if the preferred camera is temporarily unavailable?
Avoid showing a control that becomes invalid immediately after a fold. Derive available formats, depth, frame rates, and stabilization from the selected device after each switch.
Polish the Preview
The inner display can leave additional room around a rear-camera preview. Choose deliberately between filling the canvas and preserving the full field of view with controls beside it. Set AVCaptureVideoPreviewLayer.videoGravity according to that choice rather than stretching the layer.
For the square ultrawide front sensors, Apple recommends using the device's dynamicAspectRatio to choose a landscape aspect ratio when appropriate. Also adopt AVCaptureDeviceRotationCoordinator so preview and captured output stay upright when the app moves between displays. Recreate the rotation coordinator when the capture device changes.
After rotation coordination is active, Apple's Duo camera guidance recommends disabling camera-sensor-orientation compensation for better performance:
photoOutput.isCameraSensorOrientationCompensationEnabled = false
Apply that setting only after the app owns the rotation path and has verified photo and video orientation on real hardware.
Testing Matrix
Test the camera as a sequence, not a collection of screenshots:
- Launch closed, start the preview, then open Duo while the preview is active.
- Launch open, close it, and verify the app follows the expected display and camera.
- Turn an open device around so a rear camera becomes forward-facing.
- Start a capture before each transition and verify the documented continue, pause, or finish behavior.
- Compare preview orientation, output orientation, and mirroring for every selected camera.
- Exercise denied permission, interrupted sessions, incoming calls, backgrounding, and thermal pressure.
- Check outer, inner, partially folded, and Split View layouts with large text and VoiceOver.
- Verify the physical-camera feature fallback on a normal single-display iPhone.
Use the Duo simulator for layout and direction development, then verify sensor formats, performance, switching latency, and output orientation on physical hardware. Simulator success is not camera-quality evidence.
Common Mistakes
- Replacing a working virtual-device flow merely because the app runs on Duo.
- Treating
.frontas a guarantee that a camera faces the user. - Mutating
AVCaptureSessioninside the main-actor direction callback. - Failing to retain the direction coordinator for the lifetime of its preview.
- Using one coordinator for two simultaneous preview views.
- Assuming capability continuity when switching from outer to inner camera.
- Mirroring based only on device position rather than the direction relative to the view.
- Testing only the open and closed endpoints, never an active transition.
Production Checklist
- [ ] The existing front-camera path uses discovery and works on single-display iPhones.
- [ ] Physical cameras are used only for a documented capability requirement.
- [ ] Session configuration runs on a dedicated actor or serialized queue.
- [ ] Direction callbacks send descriptors across the concurrency boundary.
- [ ] Switching behavior is defined for preview, photo, and active video capture.
- [ ] Mirroring, rotation, focus, zoom, and available controls update after a switch.
- [ ] Preview layout respects safe areas, reserved regions, and vertical controls.
- [ ] Permission denial and session interruption preserve a recoverable UI.
- [ ] Real-device tests cover every camera and display transition the product supports.
Related
- Decide how much iPhone Duo-specific work your app needs
- Keep navigation and drafts stable on iPhone Duo
- Build fold-safe Duo toolbars
- Audit layouts for live resizing
- PhotoKit limited access and iCloud loading