Skip to content
Mobile
Skill

/iphone-duo-camera

Build camera capture for iPhone Duo's two front cameras — the virtual front camera, the inner under-display and outer ultra-wide devices, AVCaptureDeviceDirectionCoordinator, the rotation coordinator, preview mirroring and video gravity, and dual-display capture. Use when an app

From plugin
iphone-duo-skills
31011 skills

Claude Code

$ npx -y skills add mirzaaghazadeh/iphone-duo-skills --skill iphone-duo-camera --agent claude-code

SKILL.md

iphone-duo-camera.SKILL.md
name: iphone-duo-camera
description: Build camera capture for iPhone Duo's two front cameras — the virtual front camera, the inner under-display and outer ultra-wide devices, AVCaptureDeviceDirectionCoordinator, the rotation coordinator, preview mirroring and video gravity, and dual-display capture. Use when an app captures photo or video and must handle the device opening, closing or flipping.

Camera on iPhone Duo

iPhone Duo is the first iPhone with **two front cameras**. Both are square sensors with an ultra-wide field of view: one on the outer display, and one beneath the inner display — the first under-display camera on iPhone.

That creates a problem no previous iPhone had. "Front" no longer means "pointing at the user."

Pick a strategy first

Everything downstream follows from this choice.

The virtual front camera — automatic, limited

Discover a front camera the ordinary way — an `AVCaptureDevice.DiscoverySession` requesting position `.front` with a Wide or Ultra Wide device type — and on iPhone Duo you resolve to a new **virtual front camera**. It switches between the two physical cameras by itself: inner camera when open, outer camera when closed.

It always uses the most relevant camera, and you write no switching code.

The cost is that it exposes only what **both** physical cameras can do:

  • max **1080p**, **60 fps**
  • **no depth**

If the app needs 4K, high frame rates, or any depth-based feature, this is not enough.

The individual cameras — full capability, your responsibility

Address each physically, via the built-in **outer** ultra-wide and built-in **inner** ultra-wide device types:

| | Inner (under-display) | Outer | |---|---|---| | Max video | 1080p @ 60 fps | 4K @ 120 fps | | Depth | supported | supported |

Full capabilities, but now **you** must switch cameras when the user opens or closes the device. Which brings us to the hard part.

Which way is a camera actually facing?

`AVCaptureDevice` has always had a fixed `position` — on iPhone, `.back` or `.front`. Both of iPhone Duo's front cameras report `.front`. That property describes where a camera sits **on the hardware**, and on a device whose displays can face opposite directions it no longer tells you where the camera **points relative to the person looking at your UI**.

Three situations make this concrete:

  • The user is looking at the inner display while the app streams the outer front

camera — a "front" camera facing *away* from them.

  • The device closes while that camera is streaming, and the same camera swings

around to face them directly.

  • The device is open and flipped, so a *rear* camera is now the selfie camera.

AVCaptureDeviceDirectionCoordinator

`AVCaptureDeviceDirectionCoordinator` (in **AVKit**) answers the real question: which cameras are forward-facing *right now*, relative to a given view. Build one from three things — your app's `UIView`, the device types to monitor, and a change handler — and it keeps you current as the device moves.

Because it is anchored to a view, it reports facing **relative to the display that view is on**. When the view is on the outer display, the outer front cameras are forward-facing and the rear cameras are backward-facing. Open the device, the view moves to the inner display, your handler fires, and now the inner front camera is forward-facing while the outer front *and* rear cameras are backward-facing.

**Driving both displays at once?** Create **one coordinator per view**. Each reports relative to its own display, so the same rear camera is forward-facing to the outer view's coordinator and backward-facing to the inner view's. That is correct, not contradictory — it is the whole point. (Dual-display camera UI comes from the scene accessories API; see `../iphone-duo-hinge-and-scenes/SKILL.md`.)

Concurrency

The coordinator is tied to a view, so it is **isolated to the main actor**. Your change handler must not call AVFoundation APIs directly.

It never hands you an `AVCaptureDevice`. The handler receives an **`AVCaptureDeviceDirectionMap`**, which sorts the tracked cameras into two arrays:

var forwardFacingDeviceDescriptors: [AVCaptureDeviceDescriptor]
var backwardFacingDeviceDescriptors: [AVCaptureDeviceDescriptor]

The same map is also readable at any time from the coordinator's `deviceDirections` property, so you aren't limited to reacting inside the handler.

An **`AVCaptureDeviceDescriptor`** is a sendable stand-in for a capture device, carrying `uniqueID`, `localizedName`, `deviceType`, `mediaTypes` and `position`. Pass a descriptor to your camera actor and construct the `AVCaptureDevice` there. That is the intended path; don't try to smuggle a device across the boundary.

Note that both of these types live in **AVKit**, alongside the coordinator — not in AVFoundation, where you might reasonably go looking first.

Because you get *arrays* rather than a single device, your handler has to choose: typically the first forward-facing descriptor whose `deviceType` matches what you're streaming. Don't assume exactly one camera faces forward.

What the handler should do

1. Reconfigure the `AVCaptureSession` to keep streaming from whichever camera is now forward-facing. 2. Re-evaluate **mirroring**. Mirroring should follow the *direction*, not the position: when a rear camera has become the forward-facing one, mirror the preview so it feels like a normal selfie view. 3. Apply any UI updates the camera change implies.

Preview polish

**Streaming the rear camera's full field of view on the inner display** leaves extra room around the preview. Two reasonable choices: offset the preview and group controls in the space left over, or let the preview fill the display. Control this with `videoGravity` on `AVCaptureVideoPreviewLayer`.

**Streaming an ultra-wide front camera**, exploit the square sensor — set `dynamicAspectRatio` on the `AVCaptureDevice` to pick a landscape aspect ratio that fills

Read more
@mirzaaghazadeh
Ships withiphone-duo-skills

Image © Apple Inc., from the iPhone Duo developer page, used for identification. Not affiliated with or endorsed by Apple. Agent skills for building iOS apps on iPhone Duo — Apple's first foldable iPhone, announced September 9, 2026.

Stars on GitHub

Momentum

stars in total
310
Star history of mirzaaghazadeh/iphone-duo-skills on GitHub, a chart by star-history.com

Other skills on iphone-duo-skills.

More skills from other plugins.

deep-code-review

deep-code-review

In-depth design-focused code review - understands codebase context before evaluating PR changes, posts structured feedback to…

expo5d53k
@expo@expoInstall Skill
modlens

modlens

Plug-in vision for text-only models. Hard rule: when a file path or URL with an image extension (.png, .jpg, .jpeg, .webp, .gif,…

modlens3w4.6k
@liustack@liustackInstall Skill
appllama-app-design-skill

appllama-app-design-sk…

Build native-feeling, benchmark-quality mobile app screens (Expo / React Native). Use when designing or implementing any mobile…

appllama2mo3.9k
@appllama@appllamaInstall Skill

Questions about iphone-duo-camera.

Which coding agents can use iphone-duo-camera?
iphone-duo-camera installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.