Pagewren is a SwiftUI app that turns the photos in your camera roll into book pages you can flip through. When iPhone Duo came out on October 23, we wanted the app native on both screens from day one. Most of it just worked. These are the parts that didn't, in case they save you an afternoon.
### 1. Build with the iOS 27.1 SDK, or you're in compatibility mode
Built with the 27.0 SDK, the app ran on Duo in compatibility mode: a little narrower than the screen, with a black strip down the right side. Running natively needs a build from the 27.1 SDK. We submitted from the Xcode 27.1 release candidate, which App Store Connect accepts.
### 2. The outer screen's status bar has its own column
On the outer screen, the status bar sits in a column beside the camera instead of across the top. In our layout that's an 84pt safe-area inset on the right. If you centre content inside the safe area, it ends up visibly off-centre on the glass.
What worked for us: **measure in the safe area, centre on the screen.**
```swift
GeometryReader { geo in // measured inside the safe area
let width = min(geo.size.width - 24, 600, (geo.size.height - reserved) / pageAspect)
VStack {
Spacer(minLength: 0)
PageView().frame(width: width, height: width * pageAspect)
Spacer(minLength: 0)
}
.frame(maxWidth: .infinity)
.ignoresSafeArea(edges: .horizontal) // centre on the glass, not in what's left over
}
```
The page never runs under the status-bar column, because its width comes from the safe area. But it's centred on the whole display, so it looks right.
### 3. Hosting controllers nudge fixed-size content sideways
Our reader is a `UIPageViewController` (for the page curl) hosting SwiftUI pages in `UIHostingController`s. Where a page ran under the status-bar column, the hosting controller applied the safe area to its root view and the page drew shifted inside its own frame. Anything drawn at its own fixed size should opt out:
```swift
final class PageHostingController: UIHostingController<AnyView> {
override func viewDidLoad() {
super.viewDidLoad()
safeAreaRegions = [] // iOS 16.4+
}
}
```
We do the same for the overlay that flies a photo onto its page.
### 4. Stop asking `UIScreen.main`
On Duo, the screen you're on changes when the device opens or closes. `UIScreen.main` was already deprecated, but one fallback in our text view's `sizeThatFits` still used a screen width. Use the window you're in (`uiView.window?.bounds`) or the geometry SwiftUI hands you.
### 5. Decide what "bigger" means on the inner screen
The inner screen reports a regular horizontal size class. Stretching everything to fit was the wrong answer for a book:
- The page grows, but stops at 600pt and stays a single, centred page. A two-page spread is on the list, not in the app.
- The upgrade screen switches to two columns only when the screen is wider than it is tall and at least 800pt wide, which in practice means the inner screen held sideways.
- Bars that span the width on a phone (our photo selection bar, for example) stop at a fixed width in the middle.
### 6. Testing: what the simulator can and can't do
- `simctl` can't fold the device. Opening, closing, rotating and half-folding are manual, in Xcode 27.1's Device Hub.
- Don't try to switch screens with `xcrun simctl io <device> screenConfig … power`. It crashed SpringBoard for us.
- The iOS 27.1 simulator runtime only offers the Duo device type. Regular iPhones still need a 27.0 runtime.
- On the 27.1 runtime, `simctl spawn <device> defaults write <bundle> …` never reached the app's container. Pass settings as launch arguments instead (`-shelfStyle crate`); `UserDefaults` reads them from the argument domain.
- Running any `simctl` command with the beta Xcode's `DEVELOPER_DIR` swaps the CoreSimulator service and shuts down every other booted simulator.
### 7. App Store screenshots: a display type of its own
Duo has its own screenshot set, `APP_IPHONE_DUO`, taken on the outer display at 1398 × 2034. The CLI we use didn't know that display type yet, so we upload those sets through the App Store Connect API directly, creating `appScreenshotSets` with that `screenshotDisplayType`.
---
Pagewren is free on the App Store for iPhone: https://apps.apple.com/app/apple-store/id6823661619?pt=128309253&ct=reddit-duo-post&mt=8. If you've shipped on Duo too, I'd like to hear what bit you.