Files
coach-ios/docs/routine-watch-runbook-mac.md
Sylvain Bettinelli caeac37f38 feat(watch): routine quotidienne cochable sur l'Apple Watch
Pendant natif du backend coach_sportif (commit 772385b, déjà en prod).

- CoachRoutineBridge (target App) : pousse blocs + état du jour vers la Watch
  via updateApplicationContext, relaie les coches Watch vers /api/routine/day
  puis notifie la WebView (event routineUpdated)
- CoachWatch/RoutineStore : état persisté en UserDefaults, reset au changement
  de jour. Les blocs viennent de l'iPhone — rien codé en dur côté watchOS, donc
  modifier la routine côté web ne demande aucun rebuild
- CoachWatch/RoutineView : liste cochable, progression, haptique, bouton de
  renvoi si la synchro a échoué
- ConnectivityManager (watch) : sendRoutine() en sendMessage avec repli
  transferUserInfo (coche faite iPhone hors de portée -> livraison différée)

⚠️ CoachLiveBridge touché : routeIfNotLiveSample() aiguille les messages
routineDone vers NotificationCenter. WCSession.delegate est unique côté iPhone,
impossible d'en ajouter un second. Le flux live workout n'est pas modifié mais
doit être re-vérifié au build.

Les 3 nouveaux fichiers sont référencés dans project.pbxproj (bonne target) :
aucun "Add Files" à faire sur le Mac. Runbook : docs/routine-watch-runbook-mac.md

NON COMPILÉ — nécessite une session Xcode sur le Mac mini.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 14:22:33 +00:00

127 lines
5.5 KiB
Markdown

# Runbook Mac — Routine quotidienne sur l'Apple Watch
**Écrit côté dev (Linux) le 2026-08-03. Rien n'est compilé.** Ce document dit
quoi faire sur le Mac pour activer la fonctionnalité, et comment la tester.
## Ce que ça fait
La routine anti-douleurs (6 blocs) apparaît sur la montre, cochable au doigt.
Une coche faite sur la Watch remonte à l'iPhone, qui la POSTe au serveur — donc
la page `/routine` et le widget de la home reflètent immédiatement l'état, et
inversement.
## Architecture (à connaître avant de débugger)
```
page /routine (web) Apple Watch (RoutineView)
│ coche │ coche
▼ ▼
POST /api/routine/check ConnectivityManager.sendRoutine
│ │ sendMessage / transferUserInfo
▼ ▼
data/routine_log.json ◄────────── CoachLiveBridge (délégué WCSession)
▲ POST /api/routine/day │ routeIfNotLiveSample()
│ ▼
└─────────────────────── CoachRoutineBridge (NotificationCenter)
│ notifyListeners
routine.js re-render
```
Trois décisions structurantes, à ne pas défaire sans raison :
1. **L'état est serveur, plus en localStorage.** La Watch n'a aucun accès au
localStorage du WKWebView : il fallait un arbitre commun aux trois surfaces.
2. **On transmet l'état COMPLET du jour, jamais un delta.** Si un message
WatchConnectivity se perd, le suivant recale tout au lieu de laisser les deux
côtés diverger. C'est aussi pour ça que `updateApplicationContext` convient :
le système n'en garde que le dernier, ce qui est la sémantique voulue.
3. **`CoachRoutineBridge` n'est PAS `WCSession.delegate`.** `CoachLiveBridge`
l'est déjà et le délégué est unique. `CoachLiveBridge.routeIfNotLiveSample()`
aiguille les messages `routineDone` vers `NotificationCenter` — c'est le seul
endroit où j'ai touché au flux live workout (validé E2E sur device le 31/05),
et il retourne `true` sans rien émettre, donc `LiveStore` n'est pas pollué.
Les blocs eux-mêmes **voyagent avec l'état** : rien n'est codé en dur côté
watchOS. Modifier la routine côté web ne demande donc aucun rebuild.
## Étapes Mac
### 1. Récupérer le code
```bash
# Xcode FERMÉ (règle COWORK)
cd ~/coach-ios && git pull
npm install && npx cap sync ios
open ios/App/App.xcworkspace
```
### 2. Rien à ajouter dans Xcode
Les trois nouveaux fichiers sont **déjà référencés dans `project.pbxproj`**,
dans la bonne target :
| Fichier | Target |
|---|---|
| `App/CoachRoutineBridge.swift` | App |
| `CoachWatch/RoutineStore.swift` | CoachWatch |
| `CoachWatch/RoutineView.swift` | CoachWatch |
Vérification rapide si un doute :
```bash
grep -c "RoutineView.swift" ios/App/App.xcodeproj/project.pbxproj # doit valoir 4
```
(4 = PBXBuildFile + PBXFileReference + children du groupe + Sources phase.)
Backup du pbxproj avant modification : `project.pbxproj.bak-routine`.
### 3. Build
Clean Build Folder (`Cmd+Shift+K`) → Run sur l'iPhone, avec la Watch appairée.
⚠️ `CoachAuth.swift` doit exister (gitignored) : le plugin s'en sert pour
authentifier son POST vers `/api/routine/day`. Sans lui, ça ne compile pas.
### 4. Tester le cycle complet
1. Ouvrir l'app iPhone sur `/routine` — ça pousse les blocs vers la Watch.
2. Sur la Watch : app Coach → **Routine du jour** → la liste des 6 blocs doit
apparaître avec la progression.
3. Cocher un bloc sur la Watch → retour à l'iPhone, la page `/routine` doit se
mettre à jour **sans rechargement** (event `routineUpdated`).
4. Cocher un bloc sur l'iPhone → rouvrir la vue Watch, l'état doit suivre.
5. Vérifier côté serveur :
```bash
ssh ubuntu@83.228.246.229 'cat /home/ubuntu/coach_sportif/data/routine_log.json'
```
### 5. Si la Watch affiche « Ouvre l'app Coach sur l'iPhone »
C'est l'état vide normal au premier lancement : la montre n'a pas encore reçu de
blocs. Si ça persiste après avoir ouvert `/routine` sur l'iPhone :
- console Safari (Développement → iPhone → coach.hypnotruck.ch) :
```js
await Capacitor.Plugins.CoachRoutineBridge.getStatus()
```
→ `paired` et `watchAppInstalled` doivent être `true`.
- Xcode console, filtre `subsystem:ch.hypnotruck.coach category:routine`.
- `updateApplicationContext` échoue silencieusement si la session n'est pas
activée : vérifier que `CoachLiveBridge.load()` a bien tourné (c'est lui qui
active la session).
### 6. Si une coche Watch ne remonte pas
- Hors de portée de l'iPhone, c'est **normal et attendu** : `transferUserInfo`
garantit la livraison différée, la coche partira au prochain rapprochement.
La vue affiche un bouton « Renvoyer vers l'iPhone » si l'envoi a échoué net.
- Sinon : vérifier le log `POST /api/routine/day` côté Xcode (le plugin logue le
code HTTP en cas d'échec — un 401 signifie un `CoachAuth.kCoachWebToken`
périmé).
## Reste à faire (hors périmètre de ce chantier)
- Complication watchOS « Routine » : la target `CoachWatchWidgets` n'existe
toujours pas (cf. `docs/widgets-runbook-mac.md`).
- Notification de rappel sur la montre si la routine n'est pas faite à 20 h.