Comme l'app Exercice : deux boutons sur la bannière de l'écran verrouillé et dans l'île dépliée. iOS n'expose aucun geste de balayage pour révéler des actions — depuis iOS 17, ce sont des boutons intégrés (App Intents), et c'est le seul mécanisme public. - Intents `CoachTogglePauseIntent` / `CoachEndWorkoutIntent`, conformes à LiveActivityIntent (sans quoi `perform()` n'est jamais appelé). - Ils vivent dans CoachLiveActivityAttributes.swift, seul fichier déjà membre des deux targets : un fichier neuf imposerait une manip Target Membership dans Xcode, source d'erreurs répétées ici. - Commandes relayées à la montre par WCSession, avec repli transferUserInfo : isReachable retombe à false quand la séance tourne en arrière-plan profond, la commande est alors différée au réveil de l'app montre. - L'état de pause remonte depuis HKWorkoutSession (seule source fiable : la montre peut mettre en pause d'elle-même) et bascule le libellé du bouton. - Le watchdog de LiveStore est neutralisé pendant la pause : sans ça, l'absence de samples aurait affiché « connexion perdue » puis terminé l'activité. - L'app watchOS gagne le bouton Pause qui lui manquait, aligné sur le même état. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
145 lines
5.9 KiB
Markdown
145 lines
5.9 KiB
Markdown
# Build du correctif « Live Activity fantôme » — runbook Mac
|
|
|
|
**Écrit le 2026-08-11.** Concerne le commit `5e9b4a3` (*La Live Activity
|
|
restait affichée après la fin de séance*).
|
|
|
|
Compte **15 minutes**. C'est un build simple : aucun fichier nouveau, aucune
|
|
target à créer, aucune capability à ajouter. Les cinq fichiers modifiés sont
|
|
déjà membres de leurs targets (vérifié dans `project.pbxproj`).
|
|
|
|
⚠️ **La montre doit être rebuildée aussi**, pas seulement l'iPhone : le message
|
|
de fin de séance part de `CoachWatch`. Un build iPhone seul ne corrige rien.
|
|
|
|
---
|
|
|
|
## Étape 1 — Récupérer le code
|
|
|
|
**Xcode fermé** (`Cmd+Q`, pas juste la fenêtre) :
|
|
|
|
```bash
|
|
cd ~/coach-ios
|
|
git pull
|
|
git log --oneline -1 # doit afficher 5e9b4a3 ou plus récent
|
|
```
|
|
|
|
Pas besoin de `npm install` ni de `npx cap sync ios` : aucune dépendance ni
|
|
config Capacitor n'a bougé. Si Xcode râle sur `no such module 'Capacitor'`,
|
|
alors seulement, lance `npx cap sync ios` (Xcode fermé).
|
|
|
|
## Étape 2 — Build sur l'iPhone
|
|
|
|
```bash
|
|
open ios/App/App.xcodeproj
|
|
```
|
|
|
|
1. iPhone branché et déverrouillé, **Apple Watch au poignet et appairée**
|
|
2. Schéma **App** + ton iPhone comme destination
|
|
3. **Run** (`Cmd+R`)
|
|
|
|
Pas de *Clean Build Folder* nécessaire — cinq fichiers modifiés, la
|
|
compilation incrémentale suffit.
|
|
|
|
## Étape 3 — Installer la nouvelle app sur la Watch
|
|
|
|
L'app watchOS est embarquée dans l'app iPhone, mais watchOS ne la réinstalle
|
|
pas toujours tout de suite. **Le plus fiable : builder directement dessus.**
|
|
|
|
1. Schéma **CoachWatch** (menu des schémas, en haut)
|
|
2. Destination : ton **Apple Watch Ultra 3**
|
|
3. **Run** (`Cmd+R`)
|
|
|
|
Le premier lancement sur la montre peut demander 2-3 minutes et afficher
|
|
« Waiting to attach » — c'est normal.
|
|
|
|
> Alternative sans Xcode : iPhone → app **Watch** → *Apps installées* → Coach
|
|
> → attendre la réinstallation. Plus lent et moins déterministe.
|
|
|
|
**Vérifier que la montre a bien la nouvelle version** : dans les logs (étape
|
|
4), la ligne `fin de séance signalée à l'iPhone` n'existe que dans ce build.
|
|
|
|
## Étape 4 — Tester (3 minutes)
|
|
|
|
Le test se fait dans cet ordre, en gardant Console.app ouvert sur le Mac :
|
|
|
|
```
|
|
Console.app → ton iPhone dans la colonne de gauche
|
|
Filtre : subsystem:ch.hypnotruck.coach
|
|
```
|
|
|
|
1. **Démarrer** une séance depuis l'app Coach sur la Watch
|
|
→ la Live Activity apparaît sur l'iPhone (cœur + FC qui bouge)
|
|
→ log `Live Activity started`
|
|
2. **Terminer** la séance sur la Watch
|
|
→ log côté montre : `fin de séance signalée à l'iPhone (reason=session-ended)`
|
|
→ log côté iPhone : `fin de séance reçue de la Watch` puis `Live Activity ended`
|
|
→ **la Live Activity disparaît immédiatement de l'écran**
|
|
|
|
C'est ce dernier point qui valide le correctif. Avant, elle restait ~4 h avec
|
|
la dernière FC figée.
|
|
|
|
3. **Écran verrouillé** : referme l'iPhone pendant la séance, termine sur la
|
|
montre, rallume l'écran. La bannière ne doit plus être là. C'est le cas qui
|
|
échouait vraiment — le watchdog reposait sur un `Timer` qui ne tourne pas
|
|
quand l'app est suspendue.
|
|
|
|
4. Vérifie au passage que **la FC remonte toujours** sur `/live` (le flux de
|
|
samples n'a pas changé, mais `CoachLiveBridge` a été touché).
|
|
|
|
### Le cœur fantôme actuellement à l'écran
|
|
|
|
Il vient de l'ancien build. Il partira soit au premier `liveEnded` reçu (la
|
|
nouvelle app reprend la main dessus via `adoptExisting()`), soit après 6 h,
|
|
soit tout de suite si tu le fermes à la main (appui long → fermer).
|
|
|
|
## Étape 4bis — Les boutons Pause / Terminer *(build du 2026-08-11, second lot)*
|
|
|
|
La Live Activity porte maintenant deux boutons, comme l'app Exercice :
|
|
**Pause / Reprendre** et **Terminer**. Ils apparaissent sur la bannière de
|
|
l'écran verrouillé et dans l'île dynamique **dépliée** (appui long sur l'île) —
|
|
pas en mode compact, où iOS n'accepte aucun bouton.
|
|
|
|
Aucune manipulation Xcode : les intents vivent dans
|
|
`CoachLiveActivityAttributes.swift`, déjà membre des deux targets.
|
|
|
|
À tester, séance en cours :
|
|
|
|
1. **Appui long sur l'île** → les deux boutons apparaissent
|
|
2. **Pause** → la séance se met en pause sur la montre, le bouton devient
|
|
**Reprendre**, et la page `/live` ne bascule **pas** en « Connexion perdue »
|
|
(le watchdog est neutralisé pendant la pause)
|
|
3. **Reprendre** → la FC repart
|
|
4. **Terminer** → la séance s'arrête sur la montre et la Live Activity
|
|
disparaît immédiatement
|
|
|
|
⚠️ **Limite Apple, à connaître avant de crier au bug** : `WCSession.isReachable`
|
|
retombe à false quand l'app watchOS tourne sa séance en arrière-plan profond
|
|
(poignet baissé, écran éteint). La commande bascule alors sur
|
|
`transferUserInfo` : elle est **garantie mais différée**, donc elle s'applique
|
|
au réveil de l'app montre et non à la seconde. Poignet levé, c'est immédiat.
|
|
Aucune API publique ne fait mieux.
|
|
|
|
Détail mineur : en **mode test** (FC simulée), la pause bascule l'état mais la
|
|
simulation continue d'émettre des samples. Sans effet sur une vraie séance.
|
|
|
|
## Étape 5 — TestFlight *(facultatif)*
|
|
|
|
Rien de spécifique ici : c'est la procédure habituelle, étapes 7 à 9 de
|
|
`docs/SESSION-XCODE.md`. Pense à incrémenter le numéro de build.
|
|
|
|
---
|
|
|
|
## Si ça ne compile pas
|
|
|
|
| Erreur | Cause probable | Solution |
|
|
|---|---|---|
|
|
| `cannot find 'CoachAuth' in scope` | `CoachAuth.swift` absent (non versionné) | étape 2 de `docs/SESSION-XCODE.md` |
|
|
| `no such module 'Capacitor'` | sync manquant | `npx cap sync ios`, Xcode fermé |
|
|
| `value of type 'ConnectivityManager' has no member 'sendEnded'` | la target CoachWatch compile un ancien `ConnectivityManager.swift` | vérifie que le `git pull` a bien eu lieu et que Xcode n'a pas restauré un pbxproj en mémoire |
|
|
| erreur de signing sur CoachWatch | profil pas régénéré | Signing & Capabilities → décoche/recoche *Automatically manage signing* |
|
|
|
|
En cas de blocage, envoie-moi l'étape, le message exact, et :
|
|
|
|
```bash
|
|
cd ~/coach-ios && git log --oneline -3 && git status -sb
|
|
```
|