Making an app resizable gets it most of the way. The bugs that remain show up when the phone folds, turns or opens while someone is using the app. These are the ones Apple’s engineers were asked about most, with their answers.
Folding loses the user’s place
Opening or closing iPhone Duo doesn’t relaunch your app. It’s the same scene, resized, and the change reaches you as a size class and trait change. Navigation and split view containers keep their state.1 If text or scroll position resets on a fold, your code is throwing it away.
The usual cause in SwiftUI is a root that swaps whole view trees by size class:
// Every fold destroys one tree and builds the other
if horizontalSizeClass == .compact {
PhoneLayout()
} else {
WideLayout()
}
Each branch has its own identity, so local @State, focus and scroll position go with it. Change the layout inside one stable tree instead, for example with AnyLayout, or lift the state above the branch.1
Keep navigation the same way: store the path in your model, not in the layout. A flow at step 8 should still be at step 8 after the phone opens and the layout changes from one pane to two.8
The layout never settles
A layout that reads its own size, changes that size, and reads it again can loop. On iPhone Duo a fold or turn can start the loop, and the app stops responding on its old layout.
- Treat bounds, safe areas and reserved regions as inputs, and make the layout a function of their current values.
- Don’t debounce or wait for a “final” value. People pause and reverse mid-fold, and there is no fixed order of updates.
- When you read reserved regions in a
GeometryReader, don’t change the size of the view you’re measuring. That invalidates the reader and starts another pass. - Keep view identity stable. Don’t tear down a stateful subtree because of a value seen for one frame mid-transition.
GeometryReader and containerRelativeFrame are still the right tools; nothing about iPhone Duo replaces them.9
Reserved regions arrive late
reservedRegions can come back empty on the first layout passes and fill in on a later one. One developer’s probe app saw no regions for its first six to nine evaluations, then every region after that.4 Code that measures the fold once, in onAppear, never sees it.
Read regions where you lay out, every time: in onGeometryChange or inside the GeometryReader. The same goes for safe-area insets: read them in the view that uses them, from a reader that respects the safe area.
For decisions that shouldn’t flip when the fold activates, ask for inactive regions too with .includeInactive. Apple’s example is a grid that prefers an even number of columns whenever a division exists, active or not.5
Insets that aren’t symmetric
On iPhone Duo the left and right insets are rarely equal, and the side with the vertical bar changes with the pose and in Split View. Don’t compute one margin and mirror it.1
One gap remains in SwiftUI: a custom ScrollView with .scenePadding(.horizontal) adds the bar’s width on the bar’s side, so its rows don’t line up with a system Form. Apple has it as feedback FB24914585, with no workaround yet.10
Asking which display you’re on
There is no API that says inner or outer display, by design, and Apple asks you not to infer one from safe-area values.11 UIScreen.main doesn’t help either: in the simulator it keeps reporting the outer display’s 466 × 678 points while the app runs on the inner one.4 Lay out from the space the view has. See Size from the window, not the screen.
Split views and arrangements
- Three columns become two. A three-column iPad layout commonly shows two columns on iPhone Duo, with the sidebar as an overlay. In portrait it may start on the detail.1 Check that every screen is still reachable that way.
- Sidebars belong in a split view. For a list-and-detail hierarchy use
NavigationSplitVieworUISplitViewController, notArrangementView.12 - Don’t nest arrangements. Keep
ArrangementViewout ofNavigationSplitView,ListandScrollView, where part of it can become unreachable.7 - An arrangement can drop a child. When a split doesn’t fit,
ArrangementViewshows only the view with the higherlayoutPriority, or the primary one. Any action that lives only in the secondary view disappears with it. ReadsplitArrangementAxisfrom the environment to know whether the split is showing.6 - Overlay z-index bug. In iOS 27.1,
overlayArrangementZIndexdoesn’t always tell the views apart. Apple’s workaround is to wrap the primary content in aVStack.13
Grids across the fold
There’s no grid that splits itself at the fold. Pick columns per size class, then use the division region to widen the gap at the hinge so no item sits across it.14
Checklist
- Fold, open and turn the phone mid-task: is the text, scroll position and navigation still there?
- Drag the hinge slowly, then quickly. Does anything freeze or jump twice?
- Read reserved regions and insets in the view that uses them, on every pass.
- Remove any check for “inner” or “outer” display.
- Make sure every action is reachable when a split view or arrangement shows one pane.
Sources
- Apple Developer Forums: A summary of the iPhone Duo Group Lab
- Apple Developer Forums: Debouncing fold and unfold layout changes
- Apple Developer Forums: Responding to the hinge in SwiftUI
- Blake Crosley: The iPhone Duo simulator in Xcode 27.1
- Tech Talk: Strike a pose with adaptive layouts on iPhone Duo
- Apple Developer Forums: How ArrangementView behaves across display modes
- Apple Developer: Preparing your app for iPhone Duo
- Apple Developer Forums: Navigation state across two displays
- Apple Developer Forums: GeometryReader and containerRelativeFrame on iPhone Duo
- Apple Developer Forums: Matching the system's asymmetric margins
- Apple Developer Forums: Telling the inner display from the outer
- Apple Developer Forums: A persistent sidebar in a TabView app
- Apple Developer Forums: overlayArrangementZIndex on iPhone Duo
- Apple Developer Forums: Grids around the fold