Files
siop2/docs/03-architecture/adr/ADR-005-distribution-mobile.md
pr-daaif 189e8e8162 feat(mobile): recette terrain iPhone 15 Pro — 7/7 validés (ADR-005)
Expo Go (App Store) bloqué par le retard d'approbation Apple (SDK 54
contre notre SDK 57) — décision : builds natifs locaux (Xcode/Android
Studio, signature Apple ID personnel gratuite) pour la vraie recette
terrain, Expo Go conservé pour l'aperçu sans installation ailleurs.

- ADR-005 : deux canaux de distribution, managed workflow conservé.
- apps/mobile/.env : EXPO_PUBLIC_USE_RN_FETCH=1 — fix permanent d'un
  bug SDK 57 (fetch global expo/fetch incompatible avec l'upload
  multipart natif {uri,name,type}, classé à tort « réseau
  indisponible » par notre propre gestion d'erreur).
- package.json/app.json : scripts ios/android pointés sur les builds
  natifs (expo run:*) plutôt que expo start (Expo Go), identifiants
  de bundle générés par le prebuild.
- Recette iOS (iPhone 15 Pro physique) : connexion démo, scan caméra
  réel (résolution locale + rejet QR étranger), vrai mode avion (D1),
  conflit de version tranché par l'humain avec photo réellement
  téléversée (D2/D5), persistance de la file à travers fermeture et
  reconstruction de l'app, suggestions R5 au vrai modèle, purge de
  sécurité au changement de compte (ADR-003) — 7/7 validés, la
  validation terrain due depuis release/r4 est levée pour iOS.
- Android : build natif sur émulateur, passage santé complet
  (connexion, navigation, écran Scanner, résolution de référence).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 20:12:48 +01:00

6.4 KiB

ADR-005 — Distribution mobile : builds natifs + Expo Go pour l'aperçu

  • Statut : acceptée (recette terrain R4/R5, 19 juillet 2026)
  • Contexte : la recette « mode avion » sur téléphone (due depuis release/r4) a buté sur Expo Go dès la première tentative — l'app installée depuis l'App Store affiche « supported SDK 54 » et refuse le projet (SDK 57). Apple a un retard d'approbation structurel sur les nouvelles versions d'Expo Go (constaté : seul SDK 54 installable sur iPhone physique début mai 2026, alors qu'Expo publiait déjà SDK 57) — ce n'est pas un problème côté projet, et rétrograder le SDK du projet pour suivre Expo Go serait disproportionné pour un aléa hors de notre contrôle.

Décision

Deux canaux de distribution mobile, en parallèle :

  1. Builds natifs locaux (Xcode pour iOS, Android Studio pour Android) — la voie pour tester vraiment sur un appareil (mode avion réel, caméra réelle, persistance), indépendante des aléas de version d'Expo Go. Signature gratuite (Apple ID personnel → Personal Team Xcode, sans compte payeur Apple Developer Program) pour les builds de recette ; à revoir si une distribution à plusieurs testeurs (étudiants) est nécessaire.
  2. Expo Go conservé comme canal léger « sans installation » pour prévisualiser rapidement sur d'autres appareils, quand la version d'Expo Go de l'appareil correspond au SDK du projet — rien à changer, c'est le fonctionnement actuel de expo start.

Le managed workflow est conservé : ios/ et android/ restent générés à la demande par expo prebuild/expo run:* (déjà gitignorés), jamais committés.

Obstacles rencontrés en recette — et leurs correctifs

Cinq heures de recette terrain ont débusqué des problèmes réels, chacun avec un correctif ou une limite désormais documentés pour ne pas être redécouverts à froid :

  1. Nom d'appareil avec apostrophe rejeté par expo run:ios --device "iPhone d'aziz" → cibler par UDID (xcrun xctrace list devices), jamais par nom.
  2. Ne jamais court-circuiter expo run:ios/expo run:android par un xcodebuild manuel : un contournement de signature via xcodebuild -allowProvisioningUpdates a produit une app qui compilait mais crashait au lancement (dyld: Library not loaded: @rpath/React.framework/React, signal 6) — la commande officielle inclut des étapes (embed frameworks, configuration Expo) qu'un appel manuel saute silencieusement.
  3. Incompatibilité des modules précompilés (SDK 56/57) : Expo distribue par défaut des XCFrameworks précompilés pour ses modules, et React Native 0.86 précompile aussi son cœur (RCT_USE_PREBUILT_RNCORE) — ce projet, en liaison statique (pas de use_frameworks!), n'est pas compatible avec ces frameworks dynamiques. C'est la cause RÉELLE du crash ci-dessus (le contournement xcodebuild n'a fait que le révéler plus tôt). Fix : EXPO_USE_PRECOMPILED_MODULES=0 et RCT_USE_PREBUILT_RNCORE=0 avant pod install/expo run:ios — tout se recompile depuis les sources.
  4. fetch global incompatible avec l'upload multipart natif : depuis SDK 57, fetch pointe vers expo/fetch (WinterCG), qui ne comprend pas le raccourci FormData {uri, name, type} utilisé pour joindre une photo (D5) — échec Unsupported FormDataPart implementation, classé à tort comme une coupure réseau par notre gestion d'erreur (catch générique de envoyerSaisie), donc invisible et mis en boucle de retry silencieuse. Fix permanent : EXPO_PUBLIC_USE_RN_FETCH=1 dans apps/mobile/.env (committé — ce n'est pas un secret), qui restaure le fetch classique de React Native, compatible avec ce raccourci.
  5. Découverte réseau de Metro par le dev client peu fiable : un git clone de dépendance (ZXingObjC) a échoué deux fois sur timeout réseau (retry + réglages git http.postBuffer/http.lowSpeedTime ont suffi) ; l'IP LAN du Mac a changé quatre fois pendant la session ; et rouvrir l'app depuis son icône après une fermeture complète échoue souvent (No script URL provided, RCTFatal) même quand Metro tourne et est joignable — la découverte Bonjour du dev client n'est pas fiable sur ce Wi-Fi. Fix fiable : relancer via npx expo run:ios --device <udid> (ou run:android), qui transmet l'adresse de Metro explicitement par deep link (siop://expo-development-client/?url=...) au lieu de compter sur la découverte automatique.
  6. Limite de fond, pas un bug : un build dev client n'a aucun bundle JS embarqué — il ne peut PAS démarrer à froid en étant déjà hors-ligne (aucun fallback possible). La persistance de la file (D1) reste validée par un scénario réaliste et suffisant : app déjà ouverte, saisie mise en file hors-ligne, process tué puis relancé avec le réseau qui revient — la file survit et se rejoue (vérifié : un OT passé ON_HOLD après fermeture complète + reconstruction de l'app). Valider un vrai démarrage à froid totalement hors-ligne exigerait un build Release (bundle embarqué) — hors périmètre d'une recette de développement, à envisager si ce cas précis devient un critère de recette client.
  7. Détail UX, pas un bug : le badge de compte (avatar) répond à un appui long (onLongPress), pas un tap simple — c'est le déclencheur de déconnexion (app/(tabs)/journee.tsx), volontairement sans confirmation puisqu'il est purement local (jeton, file, cache — jamais le réseau, ADR-003). Vaut d'être rendu plus visible si un retour utilisateur le demande.

Conséquences

  • La recette terrain iOS (7 points : connexion, scan caméra réel avec rejet de QR étranger, vrai mode avion, conflit D2 tranché par l'humain, persistance, suggestions R5 au vrai modèle, purge de sécurité au changement de compte) est validée sur iPhone 15 Pro physique — la validation due depuis release/r4 est levée.
  • Android : build natif installé et passage santé fait sur émulateur (Pixel_3a_API_34, connexion démo, navigation, écran Scanner sans plantage, résolution de référence confirmée via la saisie manuelle — équivalent du scan sans caméra réelle). La recette terrain sur appareil Android physique (vrai mode avion, vraie caméra) reste un reste, comme pour iOS avant aujourd'hui, en l'absence d'appareil disponible.