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>
This commit is contained in:
126
docs/routine-watch-runbook-mac.md
Normal file
126
docs/routine-watch-runbook-mac.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user