Vérifié dans project.pbxproj avant d'envoyer Sylvain dans Xcode : les quatre cibles réelles sont App, CoachLiveActivity, CoachWatch et CoachWatchWidgetsExtension. Le runbook donnait le nom du DOSSIER (CoachWatchWidgets) pour celui de la cible — il aurait cherché un intitulé inexistant dans l'inspecteur de Target Membership, à l'étape précisément décrite comme celle qui décide de tout. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
108 lines
5.5 KiB
Markdown
108 lines
5.5 KiB
Markdown
# Runbook Mac — Siri + widget Hydratation de la montre
|
|
|
|
**Écrit le 25/09/2026, depuis Linux.** Le code Swift est committé ; il reste les
|
|
étapes Xcode, qui ne peuvent pas se faire d'ici.
|
|
|
|
> ⚠️ **Nom des cibles, vérifié le 25/09** : la cible watchOS s'appelle
|
|
> **`CoachWatchWidgetsExtension`** — le DOSSIER, lui, s'appelle
|
|
> `CoachWatchWidgets`. Ce runbook disait d'abord le nom du dossier ; corrigé.
|
|
> Les quatre cibles réelles : `App`, `CoachLiveActivity`, `CoachWatch`,
|
|
> `CoachWatchWidgetsExtension`.
|
|
|
|
> ⛔ **Rien de ce code n'a été compilé.** `AppIntents`, `WidgetKit` et `SwiftUI`
|
|
> sont des frameworks Apple, absents de Swift pour Linux — le CLAUDE.md de ce
|
|
> dépôt le dit. Ce qui a été vérifié à la place, par lecture croisée des
|
|
> sources : que chaque symbole appelé existe (`CoachQuickLog.append`,
|
|
> `todayTotal`, `pending`, `CoachWidgetStore.load`, `nextMidnight`,
|
|
> `fallbackQuickDrinks`, `resolvedQuickDrinks`, `waterMlToday`, `waterGoalMl`,
|
|
> `asOf`), et qu'aucun symbole n'est déclaré deux fois. **Les erreurs de type,
|
|
> de concurrence et d'API n'apparaîtront qu'au build.**
|
|
|
|
## Ce que ça apporte
|
|
|
|
| | Avant | Après |
|
|
|---|---|---|
|
|
| Siri | rien — aucun `AppShortcutsProvider` | 5 raccourcis, 16 phrases |
|
|
| Montre | complications d'affichage | widget Smart Stack qui **note un verre** |
|
|
| Intents | 1, enfermé dans l'extension iPhone | 3, partagés entre 3 cibles |
|
|
|
|
## Fichiers ajoutés / modifiés
|
|
|
|
- **`ios/App/App/CoachIntents.swift`** *(nouveau)* — `LogDrinkIntent` (déplacé),
|
|
`HydrationStatusIntent`, `TodaySessionIntent`, `CoachQuickError`.
|
|
- **`ios/App/App/CoachAppShortcuts.swift`** *(nouveau)* — les phrases Siri.
|
|
- **`ios/App/CoachWatchWidgets/CoachWatchHydration.swift`** *(nouveau)* — widget
|
|
Smart Stack de la montre.
|
|
- `ios/App/CoachLiveActivity/CoachQuickWidget.swift` — l'intent en a été
|
|
**retiré** (il vivait là, invisible des deux autres cibles).
|
|
- `ios/App/CoachWatchWidgets/CoachWatchComplications.swift` — le widget rejoint
|
|
le `WidgetBundle`.
|
|
|
|
## Étapes Xcode
|
|
|
|
⚠️ **Quitter Xcode puis `git pull` d'abord** (règle COWORK : jamais de pull
|
|
Xcode ouvert).
|
|
|
|
### 1. Target Membership — c'est l'étape qui décide de tout
|
|
|
|
| Fichier | App | CoachLiveActivity | CoachWatchWidgetsExtension |
|
|
|---|:--:|:--:|:--:|
|
|
| `CoachIntents.swift` | ✅ | ✅ | ✅ |
|
|
| `CoachAppShortcuts.swift` | ✅ | ❌ | ❌ |
|
|
| `CoachWatchHydration.swift` | ❌ | ❌ | ✅ |
|
|
| `CoachQuickLog.swift` | ✅ | ✅ | ✅ *(à vérifier)* |
|
|
| `CoachWidgetSnapshot.swift` | ✅ | ✅ | ✅ *(déjà le cas)* |
|
|
|
|
⚠️ **`CoachAppShortcuts.swift` dans la cible App UNIQUEMENT.** Apple attend un
|
|
seul `AppShortcutsProvider` par application ; un second dans une extension
|
|
produirait au mieux un doublon.
|
|
|
|
⚠️ **`CoachQuickLog.swift` doit être dans `CoachWatchWidgetsExtension`**, sinon le widget
|
|
de la montre compile mais n'écrit nulle part. Son en-tête le réclamait déjà.
|
|
|
|
### 2. Vérifier l'App Group sur la cible Watch
|
|
|
|
`group.ch.hypnotruck.coach` sur **CoachWatch** ET **CoachWatchWidgetsExtension**.
|
|
Vérifié depuis Linux le 25/09 : les quatre `.entitlements` le portent et les
|
|
`CODE_SIGN_ENTITLEMENTS` sont câblés. À reconfirmer dans Signing & Capabilities.
|
|
|
|
### 3. Build, puis les vérifications qui comptent
|
|
|
|
1. **Siri** — dire « Note un verre d'eau dans Coach ». Attendu : « Noté.
|
|
X,XX L d'eau aujourd'hui », **sans que l'app s'ouvre**.
|
|
2. **Raccourcis** — l'app *Raccourcis* doit lister les 5 actions sous « Coach ».
|
|
3. **Smart Stack** — ajouter le widget Hydratation, toucher « +25 cl », vérifier
|
|
que le total monte **et** que le badge des saisies en attente s'incrémente.
|
|
4. **Synchro** — ouvrir l'app iPhone, vérifier que la saisie faite au poignet
|
|
remonte sur `coach.hypnotruck.ch`.
|
|
5. **Minuit** — le total doit repartir de zéro (c'est `asOf(_:)` qui l'assure,
|
|
pas le réveil de la timeline : le piège a déjà été payé côté iPhone).
|
|
|
|
## Les trois questions que seul le build tranchera
|
|
|
|
1. **Une complication de CADRAN accepte-t-elle un bouton d'App Intent, ou est-ce
|
|
réservé à la Smart Stack ?** La doc Apple n'était pas récupérable depuis
|
|
Linux. Par prudence, `.accessoryCircular` n'est ici qu'un **affichage** ; si
|
|
le cadran accepte l'interactivité, on pourra l'enrichir.
|
|
2. **Les phrases Siri passent-elles la validation ?** Chaque phrase contient
|
|
`\(.applicationName)`, comme Apple l'exige, et le nom supposé est « Coach »
|
|
(`CFBundleDisplayName`). Si le nom affiché diffère, les phrases ne se
|
|
déclencheront pas — c'est silencieux.
|
|
3. **`ProvidesDialog` sur watchOS.** Les intents rendent
|
|
`some IntentResult & ProvidesDialog` pour que Siri réponde à voix haute. À
|
|
confirmer que la cible watchOS l'accepte sans ajustement.
|
|
|
|
## Ce qui a été volontairement écarté
|
|
|
|
- ⛔ **Saisie des repas depuis la montre.** Pas de caméra, pas de scanner de
|
|
code-barres, pas de saisie de texte confortable : un bouton « repas » au
|
|
poignet ne ferait que renvoyer vers l'iPhone. Décidé avec Sylvain le 25/09.
|
|
- ⛔ **Un paramètre libre de volume dans les phrases Siri.** Un intent
|
|
paramétré oblige Siri à poser une question — ce qui coûte plus de temps que
|
|
de sortir le téléphone, et vide le raccourci de son intérêt. Les volumes sont
|
|
donc fixés (250 ml, 500 ml, café 60 ml).
|
|
- ⛔ **Toute borne en bpm dans les réponses de Siri.** `TodaySession` n'en porte
|
|
pas, et il ne faut pas en ajouter : une fourchette cardiaque annoncée par
|
|
haut-parleur, hors de la recommandation du jour et de ses réserves,
|
|
reviendrait à prescrire une intensité à un post-infarctus.
|