Files
coach-ios/docs/migration-native-analyse.md
2026-06-29 05:05:14 +00:00

124 lines
8.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Analyse — migration « iOS pur et dur » (natif SwiftUI) vs hybride actuel
**Date** : 2026-06-29
**Statut** : aide à la décision (aucun code engagé)
**Demandé par** : Sylvain — « sinon on migre mon application iOS pur et dur »
**Méthode** : estimations ancrées sur l'inventaire réel du code (pas d'extrapolation).
---
## 0. TL;DR
- **Le backend ne bouge pas** dans aucun scénario : `coach_sportif` (~9 600 lignes Python) est déjà une API + des intégrations. Il sert aussi bien une WebView qu'une app native.
- **Ce qu'une migration native jette / réécrit** : tout le **front** — ~14 900 lignes de templates, ~10 000 lignes de CSS, ~2 800 lignes de JS séparé (+ beaucoup de JS inline dans les pages). Mature, itéré pendant des mois.
- **Coût réaliste d'un rewrite SwiftUI à parité** (dev solo) : **~5 à 8 mois** plein temps, **iOS seulement**. Atteindre la parité Android en plus = quasi le même effort en Compose, ou abandonner Android.
- **Recommandation** : **ne pas faire de big-bang natif.** Soit rester hybride avec des îlots natifs ciblés (déjà ta trajectoire), soit, si tu veux vraiment du natif, une **migration incrémentale page-par-page** (« strangler ») étalée — mais elle a un coût de complexité propre (cf. §6).
---
## 1. Ce que tu as aujourd'hui (hybride Capacitor)
| Brique | Volume | Sort en cas de rewrite natif |
|---|---|---|
| Backend FastAPI (`web/app.py` + `web/routers/`) | ~9 600 lignes | **Conservé** (devient l'API du natif) |
| Templates Jinja (pages) | 26 pages, ~14 900 lignes | **Réécrit en SwiftUI** |
| CSS design system (`style.css` + `critical.css`) | ~10 000 lignes | **Jeté** (refait en SwiftUI/tokens) |
| JS client (`web/static/*.js`) | ~2 800 lignes | **Réécrit en Swift** |
| JS inline dans les templates | gros (ex. `meals.html` ≈ 2 900 lignes dont bcp de JS) | **Réécrit en Swift** |
| Libs front (Chart.js, Leaflet) | vendor | Remplacé par **Swift Charts** / **MapKit** |
| Plugins natifs déjà écrits | 8 (HealthKit, WorkoutKit, Live Activity, watchOS, auth Apple/Google…) | **Conservés/réutilisés** |
| Wrapper Android + PWA Windows | partagé avec le web | **Perdu** (ou à maintenir à part) |
**Atouts hybride** que la migration sacrifie :
- **1 base UI → 3 cibles** (iOS, Android, PWA) servies par le même web.
- **Déploiement instantané sans review Apple** (l'app charge la prod en direct) : ~95 % de tes itérations ne touchent pas le Mac.
- Déjà en TestFlight, déjà fonctionnel.
---
## 2. Effort de réécriture, page par page (ancré sur le volume réel)
Échelle indicative pour **un dev solo**, à parité fonctionnelle + tests device (charts = Swift Charts, cartes = MapKit, formulaires natifs) :
### Pages lourdes (le gros du coût)
| Page | Lignes | Pourquoi c'est lourd | Estim. |
|---|---|---|---|
| `meals` | 2 878 | Scan code-barres, recherche FatSecret + Open Food Facts, fiche aliment, hydratation, analyse Coach IA, modales | **34 sem** |
| `calendar` | 2 610 | Modales séance, segments, rappels locaux, export ICS, vues semaine/jour, états « fait/auto » | **34 sem** |
| `workout` (détail) | 1 950 | Carte Leaflet (trace GPS), graphes FC/puissance/zones, laps, comparaison de trajets | **23 sem** |
| `body` | 1 084 | Graphes multi-métriques, Withings, saisie manuelle | **1.52 sem** |
| `settings` | 1 135 | Intégrations (connexions OAuth), debug plugins natifs, prefs | **1.52 sem** |
| `sleep` | 783 | Graphes sommeil, sources HealthKit | **11.5 sem** |
| `live_workout` | 323 | Temps réel (déjà fortement natif via CoachLive*) | **0.51 sem** |
### Pages moyennes
`home` (286), `forme` (428), `workouts` (402), `hrv` (396), `week` (376), `profile` (395), `stats` (232), `reviews`+`review_detail` (283) — graphes, listes, navigation. **≈ 35 jours chacune** → ~**46 sem** cumulées.
### Pages simples
`login`, `register`, `reset_password`, `privacy`, `usage`, `icons*`, `dashboard*`**0.51 j chacune** → ~**1 sem**.
### Transverse (à faire une fois, mais incompressible)
- **Design system SwiftUI** réimplémentant les tokens `--c-*`/`--radius-*` + composants `.ap-*` : **23 sem**.
- **Navigation / tab bar / TileManager** (perso. d'accueil) en SwiftUI : **12 sem**.
- **Couche réseau + modèles + auth** (consommer l'API, session JWT, gestion offline/cache) : **23 sem**.
- **Coach IA chat** (streaming SSE, outils) en natif : **12 sem**.
**Total iOS seul (parité) : ≈ 58 mois** plein temps solo, hors imprévus device/App Store. C'est un ordre de grandeur, pas un devis.
---
## 3. Ce qu'on garde / ce qu'on jette
**On garde (déjà fait, réutilisable tel quel)**
- Tout le **backend** + intégrations (Polar/Bosch/Withings/TP/FatSecret), auth multi-user JWT, Coach IA serveur.
- Les **8 plugins natifs** : HealthKit (`CoachHealthRoute`), WorkoutKit, Live Activity, watchOS (`CoachWatch`/`CoachLiveBridge`), Sign in Apple/Google. En natif ils deviennent des services directs (plus besoin du pont Capacitor).
**On jette / réécrit**
- 100 % du front web (HTML/CSS/JS), soit ~25 000 lignes cumulées de code mûr.
- L'avantage « déploiement sans review ».
- Le partage de code avec **Android** et la **PWA**.
---
## 4. Impact Android / PWA (souvent l'angle mort)
Aujourd'hui, iOS, **Android** (Health Connect) et **PWA Windows** partagent le même front web. Un rewrite **iOS natif** :
- **Casse la parité** : Android resterait sur le wrapper web → tu te retrouves avec **2 fronts** (SwiftUI + web) à maintenir en parallèle, divergents.
- Pour re-parité Android native, il faudrait **Jetpack Compose** = un second rewrite d'ampleur comparable.
- La **PWA** disparaît du périmètre natif.
→ Le coût réel d'un « tout natif » multi-plateforme n'est pas « 1 rewrite » mais potentiellement « 2 rewrites + le web maintenu pour la PWA ».
---
## 5. Risques
- **Tunnel** : 58 mois sans valeur livrable visible pendant que le produit (pivot commercial FR) doit avancer. Risque #1 pour un solo.
- **Régressions** : tu réécris des comportements subtils déjà debuggés (Safari Date, caches SW, états calendar, footguns HealthKit…). Chaque page = re-tester sur device.
- **Dépendance Mac/review** sur chaque correctif (perte de vélocité).
- **Double maintenance** pendant la transition (le natif et le web coexistent forcément un moment).
- **Coût d'opportunité** : ces mois ne vont pas dans les features qui différencient (Coach IA, agrégateur, intégrations).
---
## 6. Les 3 options, honnêtement
### Option A — Rester hybride + îlots natifs (recommandé)
Garder la WebView, ajouter du natif **uniquement là où ça paie** : widgets, complications Watch, écrans temps réel (déjà fait pour le live), animations clés. **Coût : jours/semaines par îlot. Aucune perte.** C'est ta trajectoire actuelle.
### Option B — Migration incrémentale « strangler » (si tu veux vraiment du natif)
Remplacer **page par page** : on garde la WebView comme coquille et on substitue une vue **native** pour une route donnée (le wrapper route vers du SwiftNav au lieu de la WebView). Tu commences par 12 pages à fort enjeu UX (ex. `home` + `live`), tu mesures le gain réel, tu décides de continuer ou non. **Avantage** : valeur livrable en continu, réversible, pas de big-bang. **Inconvénient** : période de cohabitation web/natif à gérer (navigation, auth, état partagé).
### Option C — Big-bang « tout natif »
Le rewrite complet décrit ci-dessus. **58 mois iOS, Android/PWA sacrifiés ou doublés.** Justifié seulement si l'app devient le cœur du business ET que la WebView est un plafond de verre prouvé (perf/UX), ET que tu as le temps/les moyens. **Pour un solo en phase de pivot : déconseillé.**
---
## 7. Recommandation
1. **Ne pas lancer de big-bang (Option C) maintenant.**
2. Si l'envie de natif vient d'**irritants précis** (perf d'une page, feeling de scroll, bug WebView) → **Option A**, on cible l'îlot. Commençons par les **widgets** (déjà cadrés) puis l'îlot qui te gêne le plus.
3. Si tu veux *stratégiquement* tendre vers le natif sans tout risquer → **Option B**, on fait **1 page pilote** (je suggère `home` ou `live`) et on juge sur pièce avant d'investir plus.
**Prochaine étape proposée** : tu choisis A ou B. En A, je reprends le scaffold des 2 widgets iPhone. En B, je scope la page pilote (archi de cohabitation WebView↔SwiftUI, auth/état partagés, routeur) avant de coder.