Do not design a separate “Duo version” of the app.
Start with an adaptive SwiftUI interface that works across continuously changing widths and heights. Add Duo-specific behavior only when the fold, hinge, second display, or vertical system bars materially improve the experience.
The target is not “Make this app support iPhone Duo.” The target is:
Make this app excellent at every size, then use Duo's unique hardware where it creates additional value.
Before reviewing or changing layout, read the full rulebook: reference.md.
When to use
- User asks to support iPhone Duo, a foldable iPhone, hinge, fold, or two displays.
- User asks for adaptive SwiftUI layout across compact → wide, including multitasking.
- Code uses
UIDevice,UIScreen.main(bounds or scale), idiom, orientation,isDuo, orisFoldedfor layout. - Work involves vertical bars, toolbar item ordering/overflow, sheet placement,
ArrangementView, reserved regions,onHingeChange/UIHingeInteraction, orCameraCaptureAccessory. - The project moves to the iOS 27.1 SDK (that build is what turns on full-screen layout and vertical bars).
Related skills
| Skill | When to load |
|---|---|
| swiftui-liquid-glass (this repo) | System bars and glass chrome on iOS 26+ |
| App Resizability (Apple, Xcode 27.1) | First-pass scan for resizing and Duo issues. Export for other agents with xcrun agent skills export |
Optimization order
- Adaptive layout
- Adaptive navigation
- Adaptive toolbars and tabs
- Fold-safe positioning
- Duo-specific arrangements
- Hinge-driven interactions
- Second-display experiences
Progressive API tiers
Classify every change before writing code. Always complete Tier 1 before proposing Tier 2 or Tier 3.
Tier 1 — Universal adaptive improvements (do these first):
NavigationSplitView
adaptive TabView
adaptive grids
ViewThatFits
AnyLayout
size classes
container-relative sizing
system toolbars
safe-area correctness
These benefit all Apple platforms and window sizes.
Tier 2 — Duo-aware layout (only when needed):
reserved regions (reservedRegions(kind:options:))
ArrangementView
vertical bar tuning (axisBehavior, visibilityPriority, compression, toolbarVerticalEdge)
toolbarVerticalBehavior(.disabled), only for the documented exceptions
sheet placement (presentationPlacement)
Duo-specific safe-area handling
Tier 3 — Duo-exclusive experiences (only when the product benefits):
onHingeChange / UIHingeInteraction
hinge angle
CameraCaptureAccessory (scene accessory, camera apps only)
multi-scene workflows (new windows only on the inner display)
Workflow
Copy and track progress:
- [ ] 1. Read reference.md (start with "Platform facts")
- [ ] 2. Check the SDK: full-screen layout and vertical bars need an iOS 27.1 SDK build (Xcode 27.1)
- [ ] 3. Review screens with the decision tree
- [ ] 4. Flag red-flag patterns with file evidence
- [ ] 5. Classify each change as Tier 1 / 2 / 3
- [ ] 6. Implement Tier 1 first
- [ ] 7. Apply Tier 2/3 only when space-based layout is insufficient; gate 27.1 APIs with #available
- [ ] 8. Keep state above adaptive layout; verify continuity across widths and every pose in Device Hub
1) Review existing UI
Walk every SwiftUI screen with the decision tree below. Flag:
- Device identity used as a layout switch (
UIDevice, idiom, model,isDuo,isFolded) UIScreen.main(bounds, scale, stored screens) or orientation used for ordinary layouthorizontalSizeClass == .regulartreated as "iPad"- Size read once (at launch or in
viewIsAppearing) and never re-read - Fixed column counts, hardcoded sidebar widths, arbitrary
if width > N - Separate compact vs expanded view hierarchies with duplicated state
- Custom toolbars / tab bars that cannot move to a vertical edge
- Toolbar items with a title but no icon, or a custom "..." menu instead of the system overflow menu
- Critical content that would sit on the fold
- Wide layouts that only stretch instead of exposing hierarchy
- Hinge angle used to decide sidebar, columns, or navigation
2) Implement or refactor
- Replace device checks with container space: size classes,
ViewThatFits,AnyLayout, adaptive grids,containerRelativeFrame/onGeometryChange. - Prefer
NavigationSplitViewfor collection → selection → detail. Prefer adaptiveTabViewover a hand-built sidebar:.tabViewStyle(.sidebarAdaptable)plus.defaultTabBarPlacement(.sidebar)opts into the sidebar on the inner display (on iPhone,.sidebarAdaptablealone shows a tab bar). - Prefer system
.toolbar/ToolbarItem/ToolbarOverflowMenuinside aNavigationStack/NavigationSplitViewso bars can become vertical. Give every item a title and an icon; put Close (.cancellationAction) first and Done (.topBarPinnedTrailing) next; setvisibilityPriority. - Keep one view hierarchy; hoist navigation, scroll, selection, editor, and playback state above layout.
- Use extra width for panes, inspectors, columns, and persistent navigation — not longer text lines. Cap readable content (e.g.
.frame(maxWidth: 700)). - Move fold-sensitive controls locally. Do not rebuild the whole screen because a reserved region appeared.
- Use
ArrangementViewonly for one two-part experience (player + playlist, editor + inspector). Do not use it as app navigation. - Use
onHingeChange/ hinge angle only for physical interaction. Layout reacts to space. Interaction may react to hinge state. For fold-aware layout, use reserved regions orArrangementView. - Do not target
innerDisplay/outerDisplayas independent canvases. The only supported outer-display content is aCameraCaptureAccessoryduring an active camera capture session. - Check every sheet in every pose; choose placement with
presentationPlacement(_:).
3) Untestable Duo behavior
Xcode 27.1 includes the iPhone Duo simulator (Device Hub: open, close, rotate, fold). It can't run most app extensions and has no camera, so camera capture accessories need a device.
Until the specific behavior can be checked in the simulator or on a device:
The agent may:
- improve general adaptability
- remove device assumptions
- adopt standard adaptive containers
- prepare code boundaries for Duo-specific APIs
- identify likely fold-sensitive UI
The agent should avoid:
- hardcoding predicted hinge coordinates
- guessing exact Duo dimensions
- adding untested Duo-only layout branches
- restructuring working screens around assumptions
If Duo-specific behavior cannot yet be verified in the simulator or on a device, prefer preparation over speculative implementation.
Agent decision tree
When reviewing a SwiftUI screen, evaluate it in this order.
-
Does the layout work across continuously changing widths?
If no: fix the general adaptive layout first. -
Is navigation manually switching between phone and tablet implementations?
If yes: investigateNavigationSplitView, adaptiveTabView, or another system navigation container. -
Are there fixed widths or screen-size assumptions?
If yes: replace them with container-relative layout where possible. -
Does the wide layout simply stretch?
If yes: look for a sidebar, detail pane, inspector, additional columns, or supplementary content. -
Could important content intersect the fold?
If yes: use reserved-region-aware positioning. -
Are two related views being manually rearranged across layouts?
If yes: evaluateArrangementView,ViewThatFits, orAnyLayout. -
Is there a custom toolbar or tab bar, or toolbar items without an icon?
If yes: determine whether SwiftUI system bars can replace it and adapt vertically, and give every item a title and an icon. -
Does the requested behavior genuinely depend on physical hinge position?
If no: do not use the hinge API.
If yes: useonHingeChange(UIKit:UIHingeInteraction) andhinge.status/hinge.angle. -
Would the outer display provide useful supplementary information during camera capture?
If yes: evaluate aCameraCaptureAccessory. Otherwise, do not try to target the second screen.
Agent rules
These are strict. Full explanations and code are in reference.md.
- When encountering device-specific layout logic, first attempt to replace it with layout behavior based on available container space.
- Prefer semantic adaptive containers over manual breakpoints. Order: system navigation/container → adaptive grid →
ViewThatFits→ size classes → exact geometry last. - If a screen contains master/detail navigation, strongly prefer
NavigationSplitViewover a manually constructedHStacksidebar. - Do not stretch narrow interfaces indefinitely. Use extra space to increase information density or introduce complementary panes.
- If repeated content appears in a grid, prefer minimum-item-width-driven adaptive columns (
GridItem(.adaptive(minimum:))). - Use
ViewThatFitswhen the question is “which arrangement fits?” rather than “which device am I on?” - Prefer changing layout containers (
AnyLayout) over conditionally rebuilding separate view hierarchies. - Never use
UIScreen.main(bounds, scale, or a stored reference) for layout. Apple discourages it and plans to deprecate main-screen references. - Avoid placing important interactive or semantic content across an active division region. Decorative backgrounds and scrolling content may usually cross the fold.
- Respond to fold interference locally before making a global structural change.
- Use
ArrangementViewonly when the two views form one adaptive experience; do not substitute it for app navigation. - Before creating a custom toolbar, verify that the design cannot be expressed with SwiftUI's toolbar APIs.
- Primary toolbar actions must remain visible; secondary actions may move into overflow as available bar space decreases.
- If tabs represent primary app sections and the wide layout benefits from persistent navigation, prefer SwiftUI's adaptive sidebar system (
.sidebarAdaptable+.defaultTabBarPlacement(.sidebar)). - Never manually mirror one safe-area inset onto another edge. Duo safe areas can be asymmetric.
- Treat the fold like the spine of a book: backgrounds may span it; precise content should not.
- Layout reacts to space. Interaction may react to hinge state. Do not use
hinge.anglefor sidebar, columns, grid count, or navigation collapse. - Use
CameraCaptureAccessoryonly for supplementary content during camera capture; keep the main experience attached to the primary scene. - A layout transition must not become an application-state transition.
- Every expansive layout must have a graceful path back through intermediate widths to compact presentation. Unfolded does not mean wide app.
- More available width does not imply wider text lines.
- Wide layouts should reveal more useful structure, not just more whitespace.
- Optimize for reach and grouping, not maximum geometric distribution.
- Always complete Tier 1 before proposing Tier 2 or Tier 3 changes.
- If Duo-specific behavior cannot yet be verified in the simulator or on a device, prefer preparation over speculative implementation.
- Check every sheet in every pose; set placement deliberately instead of building custom presentations.
- Never call a 27.1 Duo API without an availability check that matches the project's deployment target.
- Every fill-mode image or video must keep its important content visible from the narrow outer display to the wide inner display.
Code-review red flags
Flag these when used for ordinary layout decisions:
UIScreen.main.bounds
UIScreen.main.scale
UIWindow(frame: UIScreen.main.bounds)
UIDevice.current.userInterfaceIdiom == .pad
UIDevice.current.orientation / statusBarOrientation / interfaceOrientation
if orientation == .landscape
if horizontalSizeClass == .regular { iPadLayout }
bounds.height == 844
view.bounds.width - view.safeAreaInsets.left * 2
if isIPhoneDuo
if isFolded { ... }
Also flag:
- fixed large frames
- hardcoded sidebar widths
- fixed grid column counts
- custom fake toolbars (or custom
UIToolbar/UINavigationBar/UITabBarinstances) - title-only toolbar items (they never go vertical)
- custom "..." overflow menus
- content centered directly across the fold
- duplicated compact and expanded state
- layouts tested only at two widths
- excessive full-width text
- controls placed outside safe areas
.scaleAspectFill/.aspectRatio(contentMode: .fill)hero media without a focal pointtoolbarVerticalBehavior(.disabled)outside the documented exceptions, or toggled per view state
Preferred replacements are in reference.md.
Review checklist
- No
if isDuo/ idiom / orientation /UIScreenlayout branches - Layout follows container space across narrow → intermediate → wide
- List/detail uses
NavigationSplitViewwith preserved selection - Grids use
GridItem(.adaptive(minimum:))rather than fixed column counts - Local rearrangements use
ViewThatFitsorAnyLayout, not duplicate hierarchies - Wide layout exposes hierarchy (sidebar, detail, inspector, columns) instead of stretching
- Readable content has a maximum width
- App builds with the iOS 27.1 SDK; 27.1 APIs are availability-gated
- System toolbars/tabs used; items work horizontally and vertically
- Every toolbar item has a title and an icon; Close/Back first, then Done; overflow priorities set
- Important controls stay in (possibly asymmetric) safe areas
- Critical content is not centered on the fold
- State (nav, scroll, selection, editors, playback) survives fold/display changes
- Sheets checked in every pose; placement set where needed
- Split View checked with the app on the left and on the right
- Hinge APIs used only for physical interaction
- Outer display content only via
CameraCaptureAccessory, not manual display targeting - Tier 2/3 APIs added only after Tier 1, and only when testable
Additional resources
- Full 28 rules, platform facts, API reference, and before/after transformations: reference.md
- Apple: Preparing your app for iPhone Duo, Designing for iPhone Duo, Three steps to make your app shine on iPhone Duo