Field notes · iOS

From Android foldables to iPhone Duo

Notes from mapping a Pixel Fold app onto Apple's first folding iPhone, written five weeks before the SDK. Most of it carries over. The posture state machine doesn't.

Apple published the Human Interface Guidelines for iPhone Duo on 9 September, together with six tech talks, five weeks before the device ships. I have an Android app built around the Pixel Fold's tabletop posture and wanted to know what a port would take. This is the map I made: the Android vocabulary (Jetpack WindowManager, postures, window size classes, insets) against the iOS one (size classes, reserved regions, arrangements, vertical bars).

Update, 21 September 2026: the SDK is out

Apple shipped Xcode 27.1 beta on 18 September with the iOS 27.1 SDK, reference documentation and an iPhone Duo simulator that opens, closes, rotates and folds. (Xcode 27.2 beta, two days earlier, does not carry it.) The HIG page is unchanged since 9 September, so sections 2 and 4 to 9 stand as written. Nearly every T and I name survived contact with the SDK. The body below has been corrected in place and its provenance marks re-checked, so it reads as a working map rather than a forecast; this box is the record of what moved. These are the names that did not survive:

  • The fold query is plural and returns an array, and the region carries its own frame — no proxy.frame(of:) step: proxy.reservedRegions(kind: .division).first(where: \.isActive)?.frame. ReservedRegion also has margins, and frame includes them. UIKit: UIView.reservedRegions(kind:options:), type UIView.ReservedRegion.
  • AxisBehavior is ToolbarItemAxisBehavior (.automatic, .horizontalOnly, .verticalPreferred), set with ToolbarContent.axisBehavior(_:); UIKit has UIBarButtonItem.axisBehavior.
  • UIKit does not mirror the SwiftUI bar names. It uses a UIVerticalBar* family: UITraitCollection.verticalBarEdge, UIViewController.preferredVerticalBarBehavior, and UINavigationItem.verticalBarCompressionBehavior — the last being the actual API behind the compress-toolbar-or-minimise-tab-bar choice in section 6.
  • AVCaptureDeviceDirectionCoordinator lives in AVKit, and it hands you a sendable AVCaptureDeviceDescriptor, not an AVCaptureDevice. My note that .front returns a camera that switches as the device opens was wrong: position is fixed hardware, and the reference says not to read facing direction from it. Only the coordinator reports direction.
  • onHingeChange takes an isEnabled: parameter. UIHinge.Status has a fourth case, unknown; SwiftUI's angle is an Angle, UIKit's a CGFloat in radians.
  • ToolbarItemVisibilityPriority is iOS 27.0, so adapter 3 in section 10 gates one version too high.

New since I wrote this: a set of arrangement-tuning APIs the tech talks never named — splitArrangementLayoutRatio, splitArrangementLayoutSize, splitArrangementFixedLayoutSize, overlayArrangementEdge, and UIKit's UISplitArrangement.Dimension. The one to know is EnvironmentValues.splitArrangementAxis: it reports the axis the arrangement resolved to, which lets a child adapt without deriving a posture at all. That is section 4's argument with less code, and it makes section 3 even more optional than I claimed.

Both screenshot sizes are now first-party A: 1398 × 2034 outer, 2007 × 2853 inner. App Store Connect cannot accept Duo assets yet — Apple says "later this year". Simulator notes: Apple silicon and macOS 26.6+, the runtime only appears when the target OS is set to 27.1, StandBy is unavailable and most app extensions can't be run or debugged.

How this guide was written

iPhone Duo and its HIG page are real. This guide targets the iOS 27.1 SDK, which Apple has announced for Xcode 27.1 beta "later this month" and which ships with the device on 23 October 2026. It was written on 12 September, five weeks before the device and six days before the SDK: at the time there was no beta to download, no simulator and no reference documentation for the new APIs. The code blocks were pseudocode in Swift syntax, written against the names Apple used in its tech talks: a specification to check against the beta, not source to paste. Every API and number carries a provenance mark; unmarked names are existing, shipping APIs. The marks were re-checked against the shipping SDK on 21 September, so most now read A.

AApple reference documentation existsstable
Tnamed in an Apple tech talk; no reference page at the timethe few that did not survive are listed in the update above
Isyntax inferred by analogy from a talkall now resolved against Xcode 27.1
3third-party source (blogs, press)unofficial

What you can do now, without the SDK: sections 2, 4, 6 and 10 are design and architecture work — the size-class subtrees, hoisting state, replacing custom bars with system toolbars, the adapter pattern — and needed nothing that was not in Xcode 27 at the time. Sections 1, 3 and 5 were the parts to revisit once the beta landed; the update above records what changed.

1. The device and the API map

iPhone Duo is a book-style foldable like the Pixel Fold: a phone-sized outer display when closed, a wide inner display with a centre hinge when open. Held open like a book the hinge is vertical; rotated by 90° and half-folded, the hinge is horizontal and the device stands like a laptop. Every posture you support on Android exists on iPhone Duo. The outer display is wider and shorter than other iPhones, which is why the system bars sit on its side (section 6).

Closed · portrait 466 × 678 pt compact · regular Closed · landscape 678 × 466 pt compact · compact Open · hinge vertical 951 × 669 pt · book pose regular · regular Open · hinge horizontal 669 × 951 pt · tabletop regular · regular system bar region outer camera, same corner in both poses fold (division region) width · height size class
Where the system bars sit in each pose (bar placement A, point sizes 3). Status bar, Dynamic Island, navigation bar, toolbar and tab bar share one vertical region on the camera edge, except on the inner display in portrait, where bars are horizontal. The inner display is regular in both size classes in every orientation.
ConceptAndroid (Pixel 9 Pro Fold)iPhone DuoSrcNotes
Outer display6.3″, about 412 × 924 dp5.4″, 466 × 678 pt @3x = 1398 × 2034 px, panel 1398 × 2034 at 460 ppi, aspect 1.453A true 3x, like every other iPhone: one point is exactly three device pixels
Inner display8″, about 2076 × 2152 px, nearly square7.6″, 669 × 951 pt @3x = 2007 × 2853 px rendered, downscaled about 6.4% onto a 1878 × 2670 panel at 430 ppi, aspect 1.42 ≈ √23Not a whole-number scale: the effective factor is ≈2.81x, so hairlines and pixel-snapped artwork soften a little. Lay out in points as always; 2007 × 2853 is the size App Store Connect asks for
FoldFoldingFeature (state, orientation, bounds, occlusionType) from WindowInfoTracker.windowLayoutInfoA reserved region of kind division: GeometryProxy.reservedRegions(kind:options:layoutDirectionBehavior:) / UIView.reservedRegions(kind:options:); types ReservedRegion / UIView.ReservedRegionAReturns an array; each region carries frame, margins, isActive, kind, id. Active only while partially folded, zero width when flat; .includeInactive to see it flat. frame is in view coordinates (pt) and includes the margins, so there is no density conversion and no frame(of:) step
Fold statestate == HALF_OPENED / FLATRegion active / inactive (isActive)ANo FLAT-with-bounds state on iOS
Fold orientationorientation HORIZONTAL / VERTICALShape of the region frame: wider than tall means a horizontal hinge (tabletop)A—
Hinge angleSensor.TYPE_HINGE_ANGLE.onHingeChange(isEnabled:) { previous, context in … } / UIHingeInteractionAStatus closed, partiallyOpen, fullyOpen (UIKit adds unknown), plus the continuous angle: an Angle in SwiftUI, radians in UIKit. context.hinge is nil without a hinge; for effects and interactions, not layout
CamerasPunch-hole on both displays; DisplayFeature occlusionOuter camera always visible in a corner, in line with the side bar, grows into the Dynamic Island; inner camera under the display, an occlusion region only while activeA—
Size classesWindowSizeClass (compact < 600 dp, medium < 840 dp, expanded), optional; BoxWithConstraints@Environment(\.horizontalSizeClass), verticalSizeClass / UITraitCollection; mandatory: outer display compact width (regular height in portrait, compact in landscape), inner display regular × regular in every orientationADetermine layout from size classes, never from idiom or orientation
OrientationscreenOrientation, LocalConfiguration.orientation, honoured on both displaysThe inner display does not honour supported interface orientations; the outer behaves as any iPhoneAUse the window's aspect ratio if a decision is needed
System chromeStatus bar top, gesture nav bottom, on both displaysOuter display and inner display in landscape: status bar, Dynamic Island, toolbars and tab bars all on one side (the camera edge); inner display in portrait: horizontal barsA—
Two panesListDetailPaneScaffold, SlidingPaneLayout, two-pane RowNavigationSplitView / UISplitViewControllerACollapses to one column at compact width, expands on the inner display, adapts columns to the fold on its own
BarsNavigationBar / bottom navigation, TopAppBarTabView, NavigationStack + .toolbar / UITabBarController, UINavigationController—These get the vertical barA; a hand-built UIToolbar or custom bar does not
Custom two panes at the hingeRow / Column with a hinge SpacerArrangementView + .arrangementViewStyle(.split) / UIArrangementViewController + .split (UISplitArrangement)ASplits horizontally when wider than tall, vertically when taller, at the fold when folded; the axis restriction is .split.axes(.vertical); ratios and sizes are tuned with splitArrangementLayoutRatio, splitArrangementLayoutSize and splitArrangementFixedLayoutSize, and @Environment(\.splitArrangementAxis) reports the axis it resolved toA
Floating panelBox with a panel over contentArrangementView, overlay styleAStacked when flat, side by side when folded; @Environment(\.overlayArrangementZIndex), placement with overlayArrangementEdge(_:)A
InsetsenableEdgeToEdge, WindowInsets.safeDrawingsafeAreaInsets per edge, .ignoresSafeArea() on backgrounds onlyAAsymmetric on iPhone Duo; never mirror one side
Saved UI staterememberSaveable@SceneStorage—Per scene
MultitaskingSplit screen, activity embedding, opt-inEvery app takes part in Split View; two apps side by side on the inner display, plus a video-over-app stacking layoutTUIRequiresFullScreen no longer prevents resizing3
Multiple instanceslaunchMode, resizeableActivityMultiple scenes via UIApplicationSupportsMultipleScenes (first iPhone); new windows only on the inner displayTHandle UIWindowScene.ActivationAction errorsA
Rear-display useRear display mode for the cameraScene accessories (.sceneAccessory(content:), .onAvailabilityChange(perform:)); CameraCaptureAccessory for camera apps; UIKit registerSceneAccessory(_:)AAvailability is controlled by the system
Toolbar overflow, prioritiesOverflow ⋮ menuToolbarItemVisibilityPriority, ToolbarOverflowMenu / UIBarButtonItemVisibilityPriority, additionalOverflowItemsAThe HIG links to these reference pages
Custom toolbar viewsCustom action viewstoolbarVerticalEdge environment value, ToolbarItemAxisBehavior, toolbarVerticalBehavior; UIKit uses UITraitCollection.verticalBarEdge, UIBarButtonItem.axisBehavior and preferredVerticalBarBehaviorA—
Screen lookupsResources.displayMetricsDo not use UIScreen.main: ambiguous with two displays, and Apple discourages itAEnvironment, trait collection or scene bounds
BiometricsFingerprint on the power keyTouch ID rather than Face ID3Branch UI strings on LAContext.biometryType
SimulatorAVD, adb shell cmd device_state state 0|1|2, adb emu rotateXcode 27.1 Device Hub: open, close, rotate, foldAApple silicon, macOS 26.6+; the runtime only appears when the target OS is set to 27.1
Window sizes in points, before insets (sizes 3, bars A)
PoseWindowBarsEach leaf when folded
Closed, portrait466 × 678vertical, on the camera edge—
Closed, landscape678 × 466vertical—
Open, hinge horizontal669 × 951horizontal (top / bottom)669 wide × about 475 tall
Open, hinge vertical951 × 669verticalabout 475 wide × 669 tall

2. The mental model changes

On Android, foldable support is usually posture-driven: observe WindowLayoutInfo, derive tabletop, book or flat, and pick a layout per posture. Apple asks for the opposite order:

  1. Size classes first. Two layouts, compact width for the outer display and regular width for the inner, cover every pose. "Don't reinvent your app when it resizes; allow the existing layout to expand."
  2. Reserved regions second. The fold and the cameras are areas your layout adapts around, like the window controls on iPad. System containers do this on their own; custom layouts query the regions.
  3. Displacement, not rearrangement. "Avoid extreme layout changes as people fold the device… favor small adjustments over rearrangement." Move a button, widen a gap, split a pane at the fold. Don't swap in a different screen. This is an architectural requirement, not a styling one; section 4 shows why.
  4. Same functions in every pose. Controls may overflow and content may move, but nothing may exist in one pose only.
  5. Bars are vertical. The single biggest visible difference. Standard bars move to the side for free; custom bars have to follow or justify why not.

What stays the same: the postures, the "content on the standing leaf, controls on the flat leaf" tabletop idea, the need to keep interactive elements away from the hinge, and the rule that continuous scrolling content such as articles and feeds may run across the fold.

3. Deriving posture, if you still need it

Most apps don't: size classes plus system containers cover them. Apps with a bespoke pose layout (games, media, creative tools with a tabletop mode) can derive the familiar Android postures from two inputs:

Checked against the iOS 27.1 SDK in Xcode 27.1 beta

enum Posture: Equatable {
    case compact                 // outer display, or a narrow Split View slot
    case open                    // inner display, flat
    case tabletop(CGRect)        // inner display, horizontal fold; the division region's frame
    case book(CGRect)            // inner display, vertical fold
}

struct PostureReader<Content: View>: View {
    @Environment(\.horizontalSizeClass) private var width
    @ViewBuilder var content: (Posture) -> Content

    var body: some View {
        FoldReader { fold in                        // section 10: nil when flat, closed, or unsupported
            let posture: Posture = switch (width, fold) {
                case (.regular, let r?) where r.width > r.height: .tabletop(r)
                case (.regular, let r?):                            .book(r)
                case (.regular, nil):                               .open
                default:                                            .compact
            }
            content(posture)
        }
    }
}

Differences from FoldingFeature to remember:

4. Architecture: one tree, posture as numbers

This is the part of the transition that is underestimated. "Displacement instead of rearrangement" reads like a layout preference; it is a constraint on how the view layer is structured.

The Android shape

A foldable Android app almost always has a posture state machine with a screen per state:

when (posture) {                                   // Android, typical
    is Posture.Tabletop -> TabletopScreen(posture.hinge)
    is Posture.Book     -> BookScreen(posture.hinge)
    Posture.Flat        -> FlatScreen()
}

Ported literally:

The shape to avoid

switch posture {                                   // same shape in SwiftUI: wrong result
case .tabletop(let fold): TabletopScreen(fold: fold)
case .book(let fold):     BookScreen(fold: fold)
default:                  FlatScreen()
}

Each case is a different view identity. On every fold SwiftUI tears one subtree down and builds another: @State inside it resets, scroll positions jump, focus is lost, and the only animation possible is a crossfade between two screens, which is precisely the "extreme layout change" the HIG forbids. The architecture produces the violation before any design decision is made.

The shape the HIG asks for

One tree per size class, chosen once at the top. Below that branch, the tree has the same identity in every pose, and the posture changes only numbers: where a split sits, how wide a gap is, which alignment a control uses.

Builds against the iOS 27.1 SDK; FoldReader is from section 10

struct RootScreen: View {
    @Environment(\.horizontalSizeClass) private var width
    var body: some View {
        if width == .compact { CompactScreen() }     // outer display and Split View slots: one identity
        else { RegularScreen() }                     // inner display, flat or folded, either hinge: one identity
    }
}

struct RegularScreen: View {
    @Environment(\.model) private var model          // state lives above the layout
    var body: some View {
        FoldReader { fold in                         // CGRect? — nil when flat
            GeometryReader { g in
                let horizontalHinge = (fold?.width ?? 0) > (fold?.height ?? 0)
                let layout = horizontalHinge
                    ? AnyLayout(VStackLayout(spacing: fold?.height ?? 0))
                    : AnyLayout(HStackLayout(spacing: fold?.width ?? 0))
                layout {                             // same children, same identity, animated frame changes
                    PrimaryPane(model)
                        .frame(height: horizontalHinge ? (fold?.minY ?? g.size.height / 2) : nil)
                    SecondaryPane(model)
                }
                .animation(.snappy, value: fold)
            }
        }
    }
}

AnyLayout swaps the arrangement of the same children without changing their identity; ArrangementViewA does the same with system rules and system animation. Either way the children are stable.

Rules

  1. Branch on size class once, at the top. Two subtrees, compact and regular. Nothing else selects a view.
  2. Posture is data, never a branch. It feeds split positions, gaps, spacing and alignment into the regular subtree. If you find yourself writing if posture == .tabletop { SomeOtherView() }, stop.
  3. Hoist state above the layout. Models, selection, scroll position (@SceneStorage), playback, text: all above the pane views, so panes can move, resize and re-parent without losing anything.
  4. Elements that travel keep identity. A floating control that moves from a column to the centre, a panel that goes from overlay to side-by-side: same view, animated frame, or matchedGeometryEffect. Never one view disappearing while another appears.
  5. Modes are not postures. A real state machine is fine for modes (edit / preview, learn / play). Modes may change what is shown; postures may not.

What this costs

Be honest in the estimate. Kotlin and Compose to Swift and SwiftUI is a rewrite of the view layer regardless of foldables; models, parsing, networking, engines and native cores port mechanically. So the cost of this section is not a migration of the Android view layer, since that layer is not coming along, but designing the SwiftUI tree correctly before writing it, and deleting the posture state machine rather than porting it. Plan the view layer from scratch around the two size-class subtrees, budget the models as a mechanical port, and treat "port the posture switch" as the one thing on the list that must not happen.

5. Layout patterns per pose

Closed (outer display, compact width)

Same as your phone layout, with two twists: the bar is on the side and vertical space is precious; and the outer camera is always in the corner, aligned with that bar. Standard bars, safe areas and layout margins handle both. Content that scrolls stays inset; immersive, non-scrolling content (a calculator, a player, a game) may span the full width as long as it avoids the Dynamic Island and status bar.

Open, flat (inner display, regular × regular)

Show one more level of hierarchy: list and detail, document and inspector, player and queue. Mail shows list or message when closed and both when open. NavigationSplitView does this; a custom two-pane layout translates to a split ArrangementViewA. Prefer even column counts in grids. The inner display is regular in both dimensions in both orientations, so a landscape-only or portrait-only assumption produces a wrong layout in one of them.

Half-folded, hinge vertical (book pose)

The fold divides the inner display into a left and a right region. Split views keep both columns visible with an even split; alerts move to the trailing side, closer to where they will be when the device closes; grids keep their outer margins and widen the gutter over the hinge. Interactive elements stay off the fold; scrolling text may cross it.

Half-folded, hinge horizontal (tabletop / laptop pose)

The top region suits content viewed at a distance while the bottom suits interactive controls.

A vertical split arrangement puts the primary view on the standing leaf and the secondary on the flat leaf, splitting at the fold; when flat, the same arrangement splits at the middle, so folding only moves the divider. That is the pattern for video and controls, camera preview and shutter, game screen and gamepad, document and keyboard.

The overlay case

A floating panel over content (mini player, reader controls, a picture-in-picture-like preview) is the overlay arrangementA: primary atop secondary when flat, side by side when folded. Use overlayArrangementZIndexA to switch the panel between its compact floating form and its full-leaf form.

Rules for all poses

6. Bars: from top/bottom to the side

On the outer display and on the inner display in landscape, the status bar, Dynamic Island, navigation bar, toolbar and tab bar share one vertical region on the camera edge: rotate the usual bars by 90° and stack them. It stays on that side in right-to-left languages because it is aligned with the hardware. Only the inner display in portrait keeps horizontal bars. In Split View each app puts its bar on its own outer edge.A

You get this by building against the iOS 27.1 SDK and using bars owned by navigation containers: NavigationStack or NavigationSplitView with .toolbar, and TabView; in UIKit UINavigationController and UITabBarController. Standalone UIToolbar, UINavigationBar, UITabBar and hand-built bottom bars are not moved.A

Translating an Android bar
AndroidiPhone Duo
Top app bar: navigation icon, title, actionsTop of the vertical region: Back or Close first, then prominent actions (Done); the title stays with the content
Bottom navigation (3 to 5 destinations)TabView; tabs go into the same vertical region
Bottom app bar / FABToolbar items; the FAB becomes a prominent toolbar item with a high visibility priority
Overflow ⋮ menuThe system overflow menu (ToolbarOverflowMenu / additionalOverflowItemsA); reserve the ellipsis for it
Text-only actions ("Save")Prefer a symbol with a title; text-only items stay in a horizontal bar and cost vertical space
Custom action viewsMust fit the bar's fixed width or provide a vertical form; read toolbarVerticalEdgeA; declare ToolbarItemAxisBehaviorA

Housekeeping the HIG asks for:

7. Safe areas, cameras, asymmetry

8. Games and full-screen media

9. Multitasking, scenes, and the other display

10. Deployment target and fallbacks

One binary has to serve iPhones on your minimum iOS, current iPhones on iOS 27.1, and iPhone Duo. Three facts make this simpler than it looks; one pattern keeps it out of the application code.

Facts

  1. Checks are on the OS version, never on the device. iPhone Duo ships with iOS 27.1, and every iPhone on 27.1 has the same APIs. There is no "is this a Duo" test and none is needed.
  2. The new APIs are no-ops without a fold. Reserved regions return nothing, the hinge context is nilA, and an arrangement view with no active division region behaves like the stack it replaces. One code path covers a flat Duo, an iPhone 17 and an iPad.
  3. Building with the 27.1 SDK does not raise the deployment target. Choose the target from your user base as usual (iOS 26, say); gate the 27.1 APIs at runtime with #available. #if is compile-time and only matters while part of your CI still builds with an older Xcode. Once the team is on Xcode 27.1, there is no #if at all.

The pattern

Every availability check lives inside a small adapter view or modifier. Application code calls the adapter and never sees #available.

Checked against the iOS 27.1 SDK in Xcode 27.1 beta

// 1. The fold. nil on flat, closed, older iOS and non-folding devices alike.
//    Callers cannot tell the difference, which is the point.
struct FoldReader<Content: View>: View {
    @ViewBuilder var content: (CGRect?) -> Content
    var body: some View {
        GeometryReader { proxy in
            if #available(iOS 27.1, *) {
                content(proxy.reservedRegions(kind: .division).first(where: \.isActive)?.frame)
            } else {
                content(nil)
            }
        }
    }
}

// 2. Two panes. The system arrangement where it exists, a plain stack everywhere else.
struct AdaptiveSplit<Primary: View, Secondary: View>: View {
    @ViewBuilder var primary: () -> Primary
    @ViewBuilder var secondary: () -> Secondary
    var body: some View {
        if #available(iOS 27.1, *) {
            ArrangementView(primary: primary, secondary: secondary)
                .arrangementViewStyle(.split)
        } else {
            ViewThatFits {
                HStack(spacing: 0) { primary(); secondary() }
                VStack(spacing: 0) { primary(); secondary() }
            }
        }
    }
}

// 3. Toolbar overflow priority. Applied unconditionally by toolbar code; a no-op before 27.1.
extension ToolbarContent {
    @ToolbarContentBuilder
    func overflowPriority(high: Bool) -> some ToolbarContent {
        if #available(iOS 27, *) {                                        // the priority API is 27.0, not 27.1
            self.visibilityPriority(high ? .high : .low)
        } else {
            self
        }
    }
}

The hinge (onHingeChange) and scene accessories take the same treatment: one modifier each, availability inside, an empty implementation below 27.1.

Test matrix

Every row must pass before the app is called ready.

ConfigurationWhat must hold
iPhone on your minimum iOSEvery adapter takes the fallback branch; the layout is what ships today
Current iPhone on iOS 27.1APIs present but return nothing; result identical to the row above. This proves the no-op path
iPhone Duo closed, portrait and landscapeCompact subtree, vertical bar, camera-edge inset
iPhone Duo open flat, both orientationsRegular subtree, inactive fold, split at the middle
iPhone Duo half-folded, both hinge directionsActive fold; only positions and gaps change, no view is replaced
iPhone Duo Split View, left and right slotCompact subtree, bar on the app's outer edge

The legacy risk to watch is not the new APIs but the old opt-outs: apps that relied on UIRequiresFullScreen or a portrait lock to avoid ever being resized will be resized on the inner display regardlessT3. Resizing is the prerequisite; the adapters above are the extras.

11. Build, ship, and the refactoring list

Built withWhat you get on iPhone Duo T
Older SDKThe app runs in a familiar iPhone-shaped frame on the inner display
iOS 27 SDKContent extends to the left of the status bar on the inner display
iOS 27.1 SDKEdge to edge, vertical bars, arrangements, reserved regions, hinge API

Refactoring: don't, why, do

One row per Android habit that breaks on iPhone Duo; the "do" column is the acceptance criterion, to be met in every configuration of the test matrix above.

Don'tWhy it failsDo
One screen per posture, chosen from FoldingFeatureRearranges the screen on every fold and destroys view identity (section 4)Two size-class subtrees; posture feeds numbers only
Decide by orientation, idiom or screen size (displayMetrics, UIScreen.main)The inner display ignores orientation and is regular × regular in every pose; two displays make the screen ambiguousSize classes; environment, trait collection or scene bounds
Treat FLAT bounds as a divider positionNo such state on iOS; the region is inactive when flatSplit at the middle when no region is active; .includeInactive only for coarse decisions
Symmetric insets (padding(horizontal = …))Bar and camera on one edge only; a Split View neighbour on the otherPer-edge safe-area insets; backgrounds under the bars, interactive content inside
Custom bottom bar, FAB, own overflow menuThe system bar region is vertical; a custom bar is not moved and covers contentNavigation containers own the bars; every item has a symbol and a title; priorities assigned; one system overflow menu
Popover cards positioned by handMay land on the fold or under the barSystem sheets, popovers, alerts and menus
Interactive elements over the hinge; odd grid columnsThe fold divides the inner displayNothing interactive on the fold; scrolling content may cross it; even column counts
Crossfade between screens on foldThe "extreme layout change" the HIG forbidsFolding moves elements; NavigationSplitView or a split arrangement for two panes, an overlay arrangement for floating panels
Hinge angle sensor for layoutApple reserves it for interactions and effectsReserved regions and arrangements
Assume one window; rely on UIRequiresFullScreen or a portrait lockSplit View and multiple scenes; the inner display resizes the app regardlessSupport resizing; decide on multi-scene explicitly; consider scene accessories if the outer display can add value
#available scattered through viewsUnreadable, untestable, each site a fallback bugAdapters (section 10); fallback tested on the minimum iOS and on a current iPhone
Ship on tech-talk namesT and I names could change before GA, and a few didDone on 21 September: every name here is now checked against the shipping Xcode 27.1 SDK. Build with it and screenshot every pose in Device Hub

Sources

Apple HIG: Designing for iPhone Duo · Layout. Tech talks: Prepare your app for iPhone Duo · Raise the bar with iPhone Duo · Strike a pose with adaptive layouts on iPhone Duo · Leverage multiple displays and scenes on iPhone Duo · Build a great camera experience for iPhone Duo · Design for iPhone Duo. Third-party numbers 3: iPhone Duo for Developers: The 1.42 Problem, iPhone Duo: First Developer Good-to-Knows.

If you are porting one too

The beta answered the two things I most wanted to know: the split arrangement does land on the fold, and the reserved region API is reservedRegions(kind:options:layoutDirectionBehavior:). I'd still like to compare notes on what breaks in a real port. Easiest way: a message on LinkedIn, or the contact on rogerboesch.com.