Adapting for iPhone Duo: Geometry-Driven Layout with System APIs
Apps that hardcode device models or measured offsets break on foldables and split-screen; this design centralizes every decision behind size classes, safe areas, and reserved regions, so the same code path handles phones, the outer screen, and two-window split-screen without per-device branches. The documented pitfalls around stale caches, trait/bounds ordering, and parent-safe-area reads are directly reusable by any team adding a foldable profile.
The column algorithm starts from a fixed baseline and returns it for any compact width, so straight phones, the outer screen, and split-screen windows all keep their existing pixel layouts untouched. On regular widths, every dimension comes from system APIs: the vertical bar is already included in the safe area, `reservedRegions` reports hinge and camera occlusion, and `layoutGuide(for: .bar(onEdge:extent:))` positions bar content. The only hand-measured constants are status-bar icon offsets (34pt / 120pt) and two TabBar values (96 / 48), all flagged as tech debt pending real-device verification. Everything is wrapped behind a `DuoEnvironment` facade, pure-function `ColumnPolicy`, and three integration points: a compositional layout subclass that forces invalidation when width changes, a page base class that suspends bar-criterion responses during transitions, and a TabBar controller that writes `additionalSafeAreaInsets` for root pages.
Even-column grids only matter when the hinge cuts vertically; forcing even columns on portrait makes cards 1.9× the phone width, so the policy correctly keys off the actual hinge orientation rather than a blanket rule.
Skipping feature flags for adaptation is defensible: a default-off flag would preserve a known-broken regular layout, so the team let the regular branch itself be the fix.
The status-bar icon hack (34pt / 120pt) is a reminder that public API still doesn't expose status-bar contents; apps aligning custom UI to those icons need a measured-constant escape hatch and a plan to revisit it after system upgrades.
The debugging lesson is the most transferable: during fold/rotate transitions, intermediate states like "951 wide with no bar" are self-inconsistent for tens of milliseconds, so screenshots mislead; log geometry over time before changing code.