# iPhone Duo: preparare un'app

Il pieghevole di Apple esce il 23/10/2026 (preordini dal 16/10) con iOS 27.1.
Queste note vengono dal lavoro su Faraway dell'08/10/2026 e dalle pagine di
Apple lette quel giorno. Valgono per tutte le app, native o no.

## Il dispositivo

- **Chiuso**: schermo esterno 466×678 punti (1398×2034 pixel), più largo e più
  basso di un iPhone normale.
- **Aperto**: schermo interno 951×669 punti in orizzontale (2853×2007 pixel),
  con la piega a metà. Ogni metà è grande più o meno quanto lo schermo esterno.
- **Colonna di sistema a destra**: orologio, fotocamera e Dynamic Island stanno
  in una colonna di 84 punti sul bordo destro, sia chiuso sia aperto. Il
  `safe-area-inset-right` vale 84 su tutta l'altezza.
- **Posture**: chiuso, aperto, a libro (mezzo piegato), appoggiato, a tenda.
  Apple non vuole un layout per ogni postura: compatto chiuso, largo aperto.

## Cosa chiede Apple

Fonti: [Get ready for iPhone Duo](https://developer.apple.com/iphone-duo/),
[Make sure your app is ready](https://developer.apple.com/iphone-duo/prepare/),
[HIG: Designing for iPhone Duo](https://developer.apple.com/design/human-interface-guidelines/designing-for-iphone-duo),
[Preparing your app for iPhone Duo](https://developer.apple.com/documentation/technologyoverviews/preparing-your-app-for-iphone-duo).

- **Con quale Xcode si compila decide quanto schermo prende l'app.** SDK iOS 26
  o prima: aperto, l'app sta al centro con spazio vuoto intorno; chiusa, sta a
  sinistra della colonna di sistema. SDK 27: quasi tutto lo schermo, ma non la
  colonna. SDK 27.1 (Xcode 27.1): tutto lo schermo, e le barre di sistema
  (navigazione, strumenti, schede) diventano verticali sul lato destro.
- **Aprire e chiudere lascia l'utente dov'era**: stessa schermata, stesso
  stato, stesse funzioni in ogni postura.
- Aperto si può mostrare un livello in più (esempio di Apple: Mail chiuso
  mostra l'elenco o il messaggio, aperto tutti e due affiancati).
- Niente pulsanti o testi sulla piega. Nelle griglie, numero pari di colonne.
- Cambiamenti piccoli fra le posture: i controlli non devono saltare in giro.
- Niente larghezze fisse, niente decisioni sul modello di telefono, sullo
  schermo principale (`UIScreen.main`) o sull'orientamento: contano lo spazio
  disponibile e le size class. Su Duo una size class larga non vuol dire iPad.
- Immagini e video a tutto schermo con riempimento: controllare che non
  taglino cose importanti nel formato largo.
- Fotocamera: aprendo e chiudendo l'app può cambiare schermo e la fotocamera
  in uso può trovarsi a guardare dall'altra parte.
- In Xcode 27.1 c'è una skill che controlla il progetto: chiedere
  all'assistente «get my app ready for iPhone Duo», oppure esportarla con
  `xcrun agent skills export` per usarla con un altro agente.

## App Store

- Screenshot del Duo: esterno 1398×2034 (o 2034×1398), interno 2007×2853 (o
  2853×2007). Facoltativi; obbligatori restano quelli dell'iPhone «medium».
  Apple consiglia di mostrare l'app in più orientamenti.
- In App Store Connect, nei dettagli per la promozione («Helpful Details»), si
  può segnalare che l'app è ottimizzata per iPhone Duo.
- Le build compilate con Xcode 27.1 RC App Store Connect le accetta (Faraway
  1.0.4, build 11 e 12, 08/10/2026: «Pronta per l'invio»).

## Provarlo sul Mac

- **Xcode 27.1 RC** in `/Applications/Xcode-27.1-RC.app`, accanto al 27.0. Si
  usa mettendo `DEVELOPER_DIR=/Applications/Xcode-27.1-RC.app/Contents/Developer`
  davanti ai comandi; `xcode-select` non si cambia, ci lavorano altre sessioni.
- Il runtime iOS 27.1 ha **solo** il Duo
  (`com.apple.CoreSimulator.SimDeviceType.iPhone-Duo`). Ogni app crea il suo
  simulatore con un nome suo («Faraway iPhone Duo»…).
- Simulator.app non c'è più: c'è **Device Hub**
  (`Xcode-27.1-RC.app/Contents/Applications/DeviceHub.app`).
- **Il simulatore si accende dal pulsante Start di Device Hub.** Acceso solo
  con `simctl boot` non viene disegnato: l'app sembra bloccata sulla splash.
- **Aprire e piegare** solo da Device Hub, con i pulsanti in fondo alla finestra
  del dispositivo (dopo quello di rotazione): chiuso, a libro, aperto. Da riga
  di comando non si può, e `simctl io screenshot` vede solo lo schermo esterno.
- **Senza mouse, quasi tutto.** Device Hub serve solo per accendere il Duo e
  aprirlo o piegarlo. Una volta acceso da Device Hub, `simctl io <id>
  screenshot` cattura lo schermo **in uso**: aperto, quello interno a
  2853×2007, giusto per App Store Connect. Per andare in una schermata precisa
  senza toccare: una build di prova legge da `localStorage` cosa mostrare, uno
  script lo scrive nel file del simulatore ad app chiusa e rilancia l'app
  (Faraway: `docs/store/schermate/duo/LEGGIMI.md`).
- L'uso del computer (mouse e tastiera veri) blocca il Mac a Paolo e lotta con
  le finestre che usa lui: usarlo il meno possibile, solo per piegare il Duo.

## Trappole trovate

- **`UIRequiresFullScreen` va tolto dall'Info.plist.** Con la chiave, aperto,
  l'app gira in un riquadro verticale rimpicciolito al centro. Senza, prende
  tutto lo schermo interno anche se l'app è solo verticale. Su un'app solo
  iPhone la chiave non serviva a niente.
- **La piega è a metà dello schermo intero**, non a metà dello spazio utile:
  con la colonna di 84 punti a destra, due colonne uguali mettevano la piega
  dentro quella di destra. Le colonne si misurano dalla piega.
- **App web (PWA, Capacitor)**: Safari e WKWebView non dicono né dove sia la
  piega né come è piegato il telefono (WebKit non ha le viewport segments).
  Lo schermo aperto si riconosce dalla forma: largo almeno 860, alto almeno
  540, più largo che alto. La colonna di sistema arriva come
  `env(safe-area-inset-right)`: va rispettata anche in pulsanti fissi, fogli,
  dialoghi e fotocamera.
- Le `fetch` dalla WebView verso `http://localhost` del Mac, con l'SDK 27.1,
  non arrivano. Per leggere lo stato della pagina nel simulatore: la pagina
  scrive in `localStorage`, dal Mac si legge il file
  `Library/WebKit/<bundle>/WebsiteData/Default/*/*/LocalStorage/localstorage.sqlite3`
  del container dell'app (valori in UTF-16).
- **React Native / Expo**: le misure da `useWindowDimensions` (cambiano
  aprendo e chiudendo), mai `Dimensions.get('screen')` letto una volta sola;
  le zone sicure da `react-native-safe-area-context`, compreso il lato destro.
- **Expo 57 non compila con Xcode 27.1** (ExpoModulesJSI: «cannot find type
  '__ObjC::expo' in scope»): Swift 6.4 scrive i nomi C++ con `::` e lo script
  `expo-modules-jsi/apple/scripts/build-xcframework.sh` non li toglieva più
  dall'interfaccia. Correzione di Expo non ancora pubblicata
  ([expo/expo#50569](https://github.com/expo/expo/pull/50569)): in Quanto Basta
  è una patch di patch-package (`patches/expo-modules-jsi+57.1.0.patch`, una
  riga di `sed`). Dopo averla messa, cancellare i `.build-hash` dentro
  `node_modules/expo-modules-jsi/apple/Products/ExpoModulesJSI.xcframework/*/`.
  Per fare la patch: `npx patch-package expo-modules-jsi --exclude
  '\.DerivedData|/Products/|\.generated|\.xcframework-slices|\.swiftpm|package\.json$'`,
  se no ci finiscono dentro centinaia di file compilati.
- **Con barre di sistema** (native-stack con intestazione, tab bar nativa) e
  SDK 27.1, iOS le mette in verticale nella colonna di destra **anche da
  chiuso**; il contenuto però non si sposta da solo: il margine destro
  (84) va lasciato a mano. Meglio la zona sicura **della pagina** che quella
  della finestra: con due pannelli anche la barra della colonna di sinistra
  si mette in verticale sul suo lato destro, accanto alla piega. In Quanto
  Basta: `SafeAreaView edges={['right']}` di react-native-safe-area-context
  come `screenLayout` delle pile. Le barre disegnate in JS invece restano
  dove sono (Quanto Basta, 08/10).
- **Il margine va sulle pagine, mai su chi contiene le barre.** Se la tab
  bar (o una pila con intestazione) sta dentro una vista che lascia già gli
  84 punti, iOS mette le barre a sinistra della colonna con l'ora invece che
  dentro, sotto la fotocamera: da chiuso il contenuto resta largo 298 punti
  invece di 382. Giusto è come fa Impostazioni di iOS: barre e pulsanti
  nella colonna, contenuto fino al suo bordo. In Quanto Basta il margine
  della pila principale prendeva anche la schermata col menù; tolto lì con
  `layout={({ children }) => <>{children}</>}` (08/10).
- **Due pannelli, due barre.** Perché anche i comandi del pannello di destra
  finiscano nella colonna, il pannello deve avere una barra di sistema sua,
  cioè una pila propria. In React Navigation due navigatori affiancati
  nella stessa schermata fanno chiudere l'app («Another navigator is already
  registered»): la pila del pannello va in `NavigationIndependentTree`. In
  Quanto Basta: `SezioneRicette.tsx` (08/10).
- `useSafeAreaInsets()` chiamato fuori da `SafeAreaProvider` (per esempio nel
  componente che lo disegna) fa chiudere l'app all'avvio, solo in Release.
- **idb** (`idb ui tap`) conosce solo lo schermo esterno: con il Duo aperto i
  tocchi non arrivano. Per girare fra le schermate da aperto servono un link
  (`simctl openurl`) o una build di prova, come per Faraway.

