# Come si compila Quanto Basta

Fino al 2026-08-31 l'app era girata solo dentro Expo Go, che è un contenitore
con i moduli nativi già compilati da qualcun altro. Questa è la procedura per
compilarla davvero, provata e funzionante su tutte e due i sistemi.

Le cartelle `ios/` e `android/` **non stanno nel repository** (sono in
`.gitignore`): le rigenera il prebuild ogni volta che servono, a partire da
`app.json`. Se le si modifica a mano, il prebuild successivo butta via tutto.

## iOS

```bash
npx expo prebuild --platform ios --no-install
cd ios && pod install
xcodebuild -workspace QuantoBasta.xcworkspace -scheme QuantoBasta \
  -configuration Release -sdk iphonesimulator \
  -destination 'platform=iOS Simulator,id=<UDID>' \
  -derivedDataPath ./build build
```

Poi si installa e si avvia:

```bash
xcrun simctl install <UDID> ios/build/Build/Products/Release-iphonesimulator/QuantoBasta.app
xcrun simctl launch <UDID> it.duebytes.quantobasta
```

In Release il JavaScript finisce dentro il pacchetto: l'app parte da sola,
senza Metro, ed è l'unico modo di vederla come sarà davvero.

### Sull'iPhone di Paolo, firmata

Fatto per la prima volta il 2026-09-16. Serve che l'Apple ID di Paolo sia
dentro Xcode (Settings → Accounts: lo fa lui, è una password) e che il
telefono sia registrato nel team — la prima volta Xcode si rifiuta di creare
il profilo se il team non ha nemmeno un dispositivo, e il flag
`-allowProvisioningDeviceRegistration` non basta con la destinazione
`generic`: l'iPhone va aggiunto a mano nel portale, con l'UDID che dà
`xcrun devicectl list devices`.

```bash
cd ios && xcodebuild -workspace QuantoBasta.xcworkspace -scheme QuantoBasta \
  -configuration Release -destination 'generic/platform=iOS' \
  -archivePath build/QuantoBasta.xcarchive archive \
  -allowProvisioningUpdates DEVELOPMENT_TEAM=686XK96PF6 CODE_SIGN_STYLE=Automatic
xcrun devicectl device install app --device <UDID> \
  build/QuantoBasta.xcarchive/Products/Applications/QuantoBasta.app
xcrun devicectl device process launch --device <UDID> it.duebytes.quantobasta
```

Certificato e profilo li crea Xcode da solo. Due trappole: la prima volta
l'archivio può fallire con «command failed with exit code 0 but produced no
further output» — è una gara fra script paralleli, si rilancia e passa; e
l'installazione vuole la **Modalità sviluppatore** attiva sull'iPhone
(Impostazioni → Privacy e sicurezza), che riavvia il telefono.

L'iPhone di Paolo è un 17 Pro Max, UDID `00008150-000539183C47401C`.

### La trappola di iOS 27: il ciclo di vita a scene

La prima build installata sull'iPhone **si chiudeva all'istante**: crash
`EXC_BREAKPOINT` in `_UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption`.
Chi compila con l'SDK di iOS 27 (Xcode 27) deve aver adottato UIScene, e né
React Native 0.86 né Expo 57 lo fanno da soli. Nel simulatore iOS 26.5 era
solo un avviso, per questo non si era mai visto.

La soluzione è `plugins/with-ios-scene-delegate.js`, registrato in `app.json`:
sposta la finestra e l'avvio di React Native in uno SceneDelegate accodato ad
AppDelegate.swift, e scrive il manifest nell'Info.plist. È il plugin di lavoro
di expo/expo#50179, in attesa che `@config-plugins/expo-uiscene-lifecycle`
arrivi su npm (quando ci sarà, sostituisce il nostro). Ha richiesto expo
≥ 57.0.23. Il crash report si scarica con `idevicecrashreport -e <cartella>`
(brew `libimobiledevice`): quelli dell'iPhone non arrivano sul Mac da soli.

**Serve CocoaPods**, e la gemma di sistema non va: il Ruby di macOS è il 2.6 e
CocoaPods vuole almeno il 2.7. Si installa con `brew install cocoapods`.

Pesa 66 MB.

## Android

**La trappola del JDK, ed è quella che fa perdere un'ora.** Android Studio si
porta dentro un JDK 25, e con quello la build fallisce così:

```
Execution failed for task ':expo-modules-core:configureCMakeDebug[arm64-v8a]'.
> WARNING: A restricted method in java.lang.System has been called
```

Non è un avvertimento, è l'errore: dal JDK 24 in poi le chiamate a
`System.load` fuori dai moduli consentiti sono bloccate, e il plugin CMake le
usa. **Serve il JDK 17.** Sta in `~/.jdks/jdk-17.0.20.1+1`, scaricato da
Adoptium senza installarlo di sistema — l'installatore con `.pkg` chiede la
password di amministratore, il tar.gz no:

```bash
mkdir -p ~/.jdks && cd ~/.jdks
curl -sL -o t17.tar.gz "https://api.adoptium.net/v3/binary/latest/17/ga/mac/aarch64/jdk/hotspot/normal/eclipse"
tar xzf t17.tar.gz && rm t17.tar.gz
```

L'ambiente, da esportare prima di ogni comando:

```bash
export JAVA_HOME="$HOME/.jdks/jdk-17.0.20.1+1/Contents/Home"
export ANDROID_HOME=/opt/homebrew/share/android-commandlinetools
export PATH="$JAVA_HOME/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
```

L'SDK si prende dagli strumenti da riga di comando — `brew install --cask
android-commandlinetools` — senza passare dall'assistente grafico di Android
Studio:

```bash
yes | sdkmanager --licenses
sdkmanager "platform-tools" "platforms;android-36" "build-tools;36.0.0" \
  "emulator" "system-images;android-36;google_apis;arm64-v8a"
```

Il progetto deve sapere dov'è l'SDK: `android/local.properties` con
`sdk.dir=/opt/homebrew/share/android-commandlinetools`. Lo si riscrive dopo
ogni prebuild, perché la cartella `android/` viene rigenerata.

Emulatore e build:

```bash
avdmanager create avd -n QuantoBasta_Pixel -k "system-images;android-36;google_apis;arm64-v8a"
emulator -avd QuantoBasta_Pixel -no-snapshot-save -no-boot-anim &
cd android && ./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
adb reverse tcp:8081 tcp:8081     # perché l'APK di debug prende il JS da Metro
adb shell am start -n it.duebytes.quantobasta/.MainActivity
```

La prima build ci mette otto minuti. L'APK di debug pesa 162 MB, ma è gonfio
apposta: dentro ci sono i simboli e tutte le architetture. Quello che finirà
sul Play Store sarà una frazione.

## Cosa ha dimostrato la prima compilazione

Cose che dentro Expo Go non si potevano sapere, e che adesso sono verificate:
il database si apre, il carattere Caveat entra nel pacchetto e si vede,
l'icona «q.b.» è quella giusta sulla schermata del telefono, e sparisce il
pulsante flottante di Expo Go che copriva l'angolo in alto a destra
nascondendo un comando.

Su Android, con l'emulatore in inglese, si è vista per la prima volta **la
traduzione inglese a schermo**: «Recipe scaler», «Your recipe book is empty»,
«Paste the ingredients here, one per line». Fino a lì era esistita solo nei
test.

## Cosa resta, e non è stato provato

La build **firmata** per gli store: sia l'archivio iOS sia l'AAB Android
vogliono un certificato, e i certificati vogliono gli account.

L'account Apple c'è dal 16 settembre 2026: iscrizione **individuale** a nome
Paolo Alberti, Team ID `686XK96PF6`, scade il 16 settembre 2027. Non è stata
aperta da capo — esisteva già, scaduta da anni, e si è rinnovata. Lo stesso
Apple Account è membro anche del team «Crazy Solutions LLC», di un'altra
persona: in Xcode e in App Store Connect va scelto sempre il team «Paolo
Alberti».

L'identificativo dell'app è `it.duebytes.quantobasta`, registrato nel portale
Apple lo stesso giorno. In App Store Connect l'app si chiama **«Quanto Basta:
Dosaricette»**, perché «Quanto Basta» da solo era già preso da un'altra app
(che le ricerche non trovavano: Apple blocca anche i nomi di app mai
pubblicate). Sull'iPhone, sotto l'icona, resta «Quanto Basta». Su Google Play
va usato lo stesso nome dello store Apple.

L'account Google Play è stato creato lo stesso giorno: account **personale**
(non organizzazione, che avrebbe voluto un numero D-U-N-S), nome sviluppatore
«Paolo Alberti - Duebytes», ID `8440704338946855239`, proprietario
`paoloalby@gmail.com`, email pubblica `paolo.alberti@duebytes.it`. Le tre verifiche
(identità, dispositivo, telefono) sono passate il 2026-09-23, e lo stesso giorno è stata creata
l'app: «Quanto Basta: Dosaricette», pacchetto `it.duebytes.quantobasta`,
italiano come lingua predefinita, gratuita (non si potrà più rendere a
pagamento; gli acquisti in-app restano possibili), ID Play Console
`4976422739576761083`. La firma per lo store la custodisce Google (Play App
Signing): noi firmiamo con una **chiave di caricamento**, creata lo stesso
giorno.

La chiave sta fuori dal repository, in
`/Users/paolo/Server/Siti Web/_private/QuantoBasta - firma Android/`
(`upload.keystore` e `credenziali.txt` con alias e password). La build la
trova grazie alle proprietà `QB_UPLOAD_*` in `~/.gradle/gradle.properties`, e
`plugins/with-android-upload-key.js` la mette nel `build.gradle` a ogni
prebuild. Dove quelle proprietà mancano la release si firma con la chiave di
debug, come nel template: compila lo stesso, ma Google la rifiuta. Se la
chiave si perde non è una tragedia: si chiede a Google di sostituirla.
Impronta SHA-256 del certificato: `CF:9C:54:CA:…:22:72:FD`.

Il pacchetto per lo store:

```bash
cd android && ./gradlew bundleRelease
# → app/build/outputs/bundle/release/app-release.aab
keytool -printcert -jarfile app/build/outputs/bundle/release/app-release.aab
```

`app.json` blocca `SYSTEM_ALERT_WINDOW` (`android.blockedPermissions`): lo
aggiungono le librerie di Expo per lo strumento di sviluppo, all'app non
serve, e nella revisione di Google è un permesso che va giustificato. Per il dispositivo è servito un telefono Android vero in prestito,
un Redmi Note 9 Pro, con l'app Play Console aperta sull'account del
proprietario; un emulatore non passa.

### Sul Redmi, col cavo

Il Redmi Note 9 Pro prestato a Paolo (seriale adb `f556416a`) si usa col cavo.
Su Xiaomi le opzioni sviluppatore si aprono toccando 7 volte **«Versione
MIUI»** (non «numero build»), e il Debug USB sta in Impostazioni aggiuntive →
Opzioni sviluppatore. «Installa tramite USB» non è servito: `adb install -r`
funziona lo stesso. Non funziona invece `adb shell input tap` (vuole «Debug USB
(impostazioni di sicurezza)», che chiede un account Xiaomi): i tocchi li fa
Paolo, gli screenshot li fa `adb exec-out screencap -p`, i log `adb logcat`.

L'APK di prova si compila solo per arm64, che basta per i telefoni degli
ultimi anni e pesa 31 MB invece di 83:

```bash
cd android && ./gradlew assembleRelease -PreactNativeArchitectures=arm64-v8a
adb install -r app/build/outputs/apk/release/app-release.apk
adb shell monkey -p it.duebytes.quantobasta -c android.intent.category.LAUNCHER 1
```

Il primo giorno sul Redmi ha fatto uscire tre difetti che iOS non mostrava:
il tondo col «+» sotto la barra dei tasti (nessuno teneva conto
dell'inset in basso), il `.quantobasta` che il gestore file di Xiaomi non
offriva di aprire con l'app (i filtri guardavano il tipo, e Xiaomi per
un'estensione sconosciuta non ne manda uno utile: ora c'è anche il filtro sul
nome, con `host="*"`), e la copia di sicurezza che falliva sempre perché
expo-file-system su Android non accetta un percorso senza `file://`.

Con l'account personale, prima della produzione Google impone un **test chiuso
con almeno 12 tester per 14 giorni**. È anche l'unico collaudo su Android vero
che l'app avrà, quindi non è tempo perso.

La fotocamera, la condivisione e l'importazione di un archivio non sono state
provate sull'app compilata. Sono i tre moduli nativi che restano senza
collaudo.
