Checklist a fasi (stringhe, hub/sheet, wizard, broadcast, bump versione) allineata al rilascio sul telefono. Co-authored-by: Cursor <cursoragent@cursor.com>
204 lines
8.6 KiB
Markdown
204 lines
8.6 KiB
Markdown
# Gap Android → iOS: allineamento app nativa
|
||
|
||
Documento operativo per continuare su **Mac** lo sviluppo iOS e rilasciare un’app **allineata ad Android `2.0.4-native`**.
|
||
|
||
**Aggiornato:** 24 luglio 2026
|
||
**Riferimento Android (produzione / telefono):** `2.0.4-native` (`versionCode` **25**), API `https://www.matchlivetv.it`
|
||
**Stato iOS attuale:** marketing `2.0.0`, build `21` — i18n **solo Login parziale**
|
||
|
||
Obiettivo iOS: **stessa copertura lingua** di Android (Login, hub, sheet/dialog, wizard, broadcast) + bump versione.
|
||
|
||
---
|
||
|
||
## Contesto
|
||
|
||
Su Android (rilasciato) la lingua scelta dall’utente vale su **tutta l’app** usabile:
|
||
|
||
- Persistenza + apply (`AppLocale` / SharedPreferences `mltv_locale` + recreate)
|
||
- Icona **globo** su Login e Matches
|
||
- Hub Partite localizzato
|
||
- Bottom sheet e dialog Matches (es. «Choose team», nuova partita, programma, elimina, riprendi)
|
||
- Wizard 3 step + branding colori
|
||
- Broadcast overlay, snackbar, dialog fine set/partita
|
||
- ~230+ chiavi × 5 lingue in `res/values*`
|
||
|
||
Su iOS esistono già `AppLanguage` + `LanguagePickerView` + Login localizzato, ma **Matches/sheet/wizard/broadcast restano in italiano hardcoded**. Va replicato il lavoro Android in SwiftUI.
|
||
|
||
API produzione: entrambe le app puntano già a `https://www.matchlivetv.it` — **nessun gap di endpoint**.
|
||
|
||
---
|
||
|
||
## Stato a confronto (target = Android 2.0.4)
|
||
|
||
| Area | Android 2.0.4 | iOS oggi | Da fare su iOS |
|
||
|------|---------------|----------|----------------|
|
||
| Persistenza lingua | SharedPreferences + wrap + recreate | UserDefaults + notification | Verificare refresh UI su **tutte** le schermate (non solo Login/Matches root) |
|
||
| Picker Login | Icona globo top-right | Testo / picker presente | Allineare a **icona globo** come Android |
|
||
| Picker Matches | Icona globo top bar | Presente | OK se già icona; altrimenti allineare |
|
||
| Login stringhe | Sì | Sì (`L10n`) | OK |
|
||
| Slogan wordmark | `app_slogan` | Chiave `app.slogan` **non usata** | Collegare `L10n.t("app.slogan")` |
|
||
| Hub Matches | Sì | Quasi tutto IT fisso | Localizzare con chiavi `matches.*` |
|
||
| Sheet/dialog Matches | Sì (`sheet_*`) | IT fisso | Localizzare (priorità: scegli squadra) |
|
||
| Status badge partita | `match_status_*` | IT | Localizzare |
|
||
| Snackbar hub | `matches_msg_*` | IT | Localizzare |
|
||
| Wizard 3 step | Sì (`wizard_*`) | IT | Localizzare |
|
||
| Branding / color picker | Sì | IT | Localizzare |
|
||
| Broadcast + score dialog | Sì (`broadcast_*`, `score_*`) | IT | Localizzare |
|
||
| Catalogo stringhe | `values` + `values-en/fr/de/es` (~230 chiavi) | ~14 chiavi in `AppLanguage.swift` | Espandere `L10n` (o migrare a `Localizable.xcstrings`) |
|
||
| Logout UI | Icona | Testo | Icona SF Symbol |
|
||
| Versione | `2.0.4-native` / **25** | `2.0.0` / **21** | Bump a **2.0.4** / **25** (o build successiva se 22–25 già usati su TestFlight) |
|
||
| RTMP ingest | `RtmpIngestUrl.kt` | `MediaUrl.swift` | OK in prod; allineare solo semantica dev se serve |
|
||
|
||
---
|
||
|
||
## File di riferimento
|
||
|
||
### Android (sorgente di verità — già fatto)
|
||
|
||
| Ruolo | Path |
|
||
|-------|------|
|
||
| Locale | `native/android/.../core/AppLocale.kt` |
|
||
| Picker | `native/android/.../ui/components/LanguagePickerDialog.kt` |
|
||
| Activity | `native/android/.../MainActivity.kt` |
|
||
| Stringhe | `native/android/app/src/main/res/values/strings.xml` + `values-{en,fr,de,es}/` |
|
||
| Login | `.../ui/login/LoginScreen.kt` |
|
||
| Hub | `.../ui/matches/MatchesScreen.kt` |
|
||
| Sheet/dialog | `.../ui/matches/MatchSheets.kt` |
|
||
| Wizard | `.../ui/wizard/*.kt` |
|
||
| Broadcast | `.../ui/broadcast/*.kt` |
|
||
| Versione | `native/android/app/build.gradle.kts` |
|
||
|
||
### iOS (da aggiornare)
|
||
|
||
| Ruolo | Path |
|
||
|-------|------|
|
||
| Lingua + L10n | `native/ios/MatchLiveTv/Core/AppLanguage.swift` |
|
||
| Login | `native/ios/MatchLiveTv/UI/Login/LoginScreen.swift` |
|
||
| Hub | `native/ios/MatchLiveTv/UI/Matches/MatchesScreen.swift` |
|
||
| Sheet/dialog Matches | file sheet/alert equivalenti sotto `UI/Matches/` (cercare stringhe IT) |
|
||
| Wizard | `UI/Wizard/` (o path equivalente) |
|
||
| Broadcast | `UI/Broadcast/` (o path equivalente) |
|
||
| Wordmark | `native/ios/MatchLiveTv/UI/Components/MatchLiveWordmark.swift` |
|
||
| Versioni | `generate_xcodeproj.py`, `project.pbxproj`, `Info.plist` |
|
||
| Build | `scripts/build_ios_release.sh` |
|
||
|
||
---
|
||
|
||
## Piano di lavoro iOS (replicare Android — ordine obbligato)
|
||
|
||
### Fase A — Infrastruttura stringhe (prima di tutto)
|
||
|
||
1. Aprire Android `values/strings.xml` e `values-en|fr|de|es/strings.xml` come catalogo.
|
||
2. Portare in `L10n.table` (o meglio `Localizable.xcstrings`) **tutte** le chiavi usate da:
|
||
- `matches_*`, `sheet_*`, `match_status_*`, `matches_msg_*`
|
||
- `wizard_*`, `wizard_color_hue`
|
||
- `broadcast_*`, `score_*`
|
||
- `login_*`, `language_*`, `action_*`, `app_slogan`, `common_*` se presenti
|
||
3. Convenzione chiavi iOS: `matches.hello` ↔ Android `matches_hello` (punto al posto di `_`).
|
||
4. Placeholder: Android `%1$s` → Swift `"Ciao, \(name)"` o `String(format: L10n.t("matches.hello"), name)`.
|
||
|
||
### Fase B — Login + wordmark (rapido)
|
||
|
||
5. Login: icona globo in alto a destra (come Android), non solo testo.
|
||
6. `MatchLiveWordmark`: `L10n.t("app.slogan")`.
|
||
7. Verificare che al cambio lingua Login si ricrei/rinfreschi (notification già presente).
|
||
|
||
### Fase C — Hub Matches + sheet/dialog (priorità UX — screenshot «Scegli squadra»)
|
||
|
||
8. `MatchesScreen.swift`: ogni `Text("…")` hardcoded → `L10n.t(...)`.
|
||
9. Sheet/dialog equivalenti a `MatchSheets.kt`:
|
||
- scegli squadra → `sheet.choose_team`
|
||
- nuova partita / programma / avvia subito
|
||
- scegli partita, badge BOZZA/PROGRAMMATA
|
||
- riprendi diretta, continua setup
|
||
- configura ora?, elimina partita
|
||
10. Snackbar/alert hub → `matches.msg_*`.
|
||
11. Badge status riga partita → `match.status_*`.
|
||
12. Assicurare `.id(languageTick)` (o environment) su root Matches **e** sheet presentati.
|
||
|
||
### Fase D — Wizard
|
||
|
||
13. Localizzare step Partita / Trasmissione / Test rete + branding editor + color hue (`wizard.*`).
|
||
14. Errori validazione e CTA AVANTI/INDIETRO/INIZIA.
|
||
|
||
### Fase E — Broadcast
|
||
|
||
15. Overlay controlli, contentDescription, snackbar pausa/ripresa/regia.
|
||
16. Dialog termina diretta, set concluso, chiudi set, partita terminata (`broadcast.*`, `score.*`).
|
||
|
||
### Fase F — Versione e release
|
||
|
||
17. Bump `MARKETING_VERSION` → `2.0.4`, `CURRENT_PROJECT_VERSION` → `25` (o successivo se conflitto TestFlight).
|
||
18. Aggiornare `generate_xcodeproj.py` **e** `Info.plist`, poi `python3 generate_xcodeproj.py`.
|
||
19. `./scripts/build_ios_release.sh` (API default già produzione).
|
||
|
||
---
|
||
|
||
## Mapping chiavi — gruppi da copiare da Android
|
||
|
||
Sorgente: `native/android/app/src/main/res/values*/strings.xml`.
|
||
|
||
| Prefisso Android | Uso | Priorità |
|
||
|------------------|-----|----------|
|
||
| `language_*`, `login_*`, `app_slogan`, `action_*` | Login / chrome | Alta |
|
||
| `matches_*` | Hub | Alta |
|
||
| `sheet_*`, `match_status_*`, `matches_msg_*` | Sheet/dialog hub | **Alta** (bug «Scegli squadra» in EN) |
|
||
| `wizard_*` | Wizard | Alta (stessa sessione utente dopo hub) |
|
||
| `broadcast_*`, `score_*` | Diretta | Alta |
|
||
| `streaming_notification_*` | Solo Android FGS | N/A su iOS |
|
||
|
||
Non lasciare fuori scope wizard/broadcast: su Android **sono già tradotti**; iOS deve seguirli per parità.
|
||
|
||
---
|
||
|
||
## Verifica manuale su Mac (acceptance)
|
||
|
||
1. `cd native/ios && python3 generate_xcodeproj.py`
|
||
2. Build simulatore; login produzione.
|
||
3. Login: globo → English → label/errori/slogan in EN.
|
||
4. Matches: saluto, CTA, empty state in EN.
|
||
5. Apri «Change» / scegli squadra: titolo **«Choose team»** (non «Scegli squadra»).
|
||
6. Nuova partita / programma / elimina: tutto EN.
|
||
7. Wizard 3 step in EN.
|
||
8. (Se possibile) overlay diretta: dialog/termina in EN.
|
||
9. Kill app → riapri: lingua persistita.
|
||
10. Spot-check FR/DE/ES.
|
||
11. Release: `./scripts/build_ios_release.sh`
|
||
|
||
Test:
|
||
|
||
```bash
|
||
cd native/ios
|
||
python3 generate_xcodeproj.py
|
||
xcodebuild -project MatchLiveTv.xcodeproj -scheme MatchLiveTv \
|
||
-destination 'platform=iOS Simulator,name=iPhone 17' \
|
||
-derivedDataPath build/DerivedData \
|
||
ONLY_ACTIVE_ARCH=YES ARCHS=arm64 EXCLUDED_ARCHS=x86_64 \
|
||
test -only-testing:MatchLiveTvTests
|
||
```
|
||
|
||
---
|
||
|
||
## Note API / produzione
|
||
|
||
| | Android | iOS |
|
||
|--|---------|-----|
|
||
| Default | BuildConfig / `-PAPI_BASE_URL` | `API_BASE_URL` → Info.plist |
|
||
| Fallback | `https://www.matchlivetv.it` | idem |
|
||
| REST / Cable | `/api/v1`, `wss://…/cable` | idem |
|
||
|
||
Nessuna modifica server richiesta.
|
||
|
||
---
|
||
|
||
## Sintesi priorità (ordine di esecuzione)
|
||
|
||
1. **A** Catalogo stringhe completo (copiare da Android `values*`)
|
||
2. **C** Hub + sheet/dialog (fix «Scegli squadra» in EN)
|
||
3. **B** Slogan + globo Login
|
||
4. **D** Wizard
|
||
5. **E** Broadcast
|
||
6. **F** Versione `2.0.4` / `25` + build release
|
||
|
||
Quando A–F sono chiusi, iOS è allineato ad Android **2.0.4-native** per UX multilingua e versioning store.
|