Ce que la session Xcode du 11 aout a rencontre, et que le guide ne disait pas

L'etape 5 est faite. Cinq ecarts entre le guide et Xcode reel, tous corriges
dans le doc pour que la prochaine session ne les repaye pas :

Le template ne s'appelle plus CoachWatchWidgets.swift mais
CoachWatchWidgetsBundle.swift. Comme il porte un @main et que
CoachWatchComplications.swift en a deja un, l'oublier fait echouer la
compilation sur deux @main dans la meme target.

Les nouvelles targets sont creees en dossier synchronise : tout fichier du
dossier en est membre automatiquement, sans etre liste dans project.pbxproj.
CoachWatchComplications.swift etait donc deja inclus - d'ou son absence du
dialogue Add Files, qui ressemble a une panne et n'en est pas une. L'etape 6
du guide est desormais sans objet, et un grep qui renvoie 0 sur ce fichier
n'est plus un signal d'alarme.

Xcode cree la target en build 1 quand le projet est en 22, ce qui bloque a
l'installation. Les cases du template ont change (Include Control et Include
Configuration App Intent au lieu de Include Live Activity) : les deux se
decochent, nos complications sont des StaticConfiguration.

Et la Watch n'a pas besoin d'etre visible comme destination dans Xcode : la
phase Embed Watch Content embarque l'app Watch dans l'app iPhone, builder le
schema App suffit. Chercher la montre dans la liste des devices est une impasse
qui coute un mode developpeur et un redemarrage pour rien.

Ajout d'un script de verification de l'assemblage : il dit a quelle target
appartient chaque phase Embed. Embed Foundation Extensions doit pointer sur
CoachWatch - rattachee a App, l'extension resterait sur l'iPhone et aucune
complication n'apparaitrait.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain Bettinelli
2026-08-11 14:34:28 +00:00
parent 4f8699fd37
commit 089b6fbde6

View File

@@ -161,24 +161,73 @@ Rien d'autre à câbler — `CoachQuickWidget()` est déjà dans le `WidgetBundl
**Tu peux sauter cette étape** et y revenir plus tard : tout le reste fonctionne
sans. Elle n'ajoute que les complications sur le cadran de la montre.
*Réalisée le 2026-08-11 — les écarts constatés sont intégrés ci-dessous.*
1. Menu **File → New → Target…**
2. Onglet **watchOS****Widget Extension****Next**
3. Product Name : `CoachWatchWidgets` — décoche *Include Live Activity*
3. Product Name : `CoachWatchWidgets`. Les cases proposées ne sont plus celles
du guide d'origine : Xcode propose désormais **Include Control** et
**Include Configuration App Intent****décoche les deux** (nos
complications sont des `StaticConfiguration`, sans paramètre à régler).
**Embed in Application** : `CoachWatch`**Finish**
4. Xcode propose d'activer le nouveau schéma → **Activate**
5. Dans le dossier `CoachWatchWidgets` créé, **supprime** le fichier
`CoachWatchWidgets.swift` généré (clic droit → Delete → *Move to Trash*)
6. Clic droit sur le dossier `CoachWatchWidgets`**Add Files to "App"…**
sélectionne `ios/App/CoachWatchWidgets/CoachWatchComplications.swift`
**décoche** *Copy items if needed*, **coche** la target `CoachWatchWidgets`
7. Sélectionne `CoachWidgetSnapshot.swift` dans la colonne de gauche →
inspecteur de droite (`Cmd+Option+1`) → **Target Membership** → coche
**CoachWatchWidgets**
5. **Supprime le fichier template.** Il ne s'appelle pas `CoachWatchWidgets.swift`
mais **`CoachWatchWidgetsBundle.swift`**, et il porte un `@main` — or
`CoachWatchComplications.swift` en a déjà un, et **deux `@main` dans une
target ne compilent pas**. Le dossier étant synchronisé (point 6), un
`rm ios/App/CoachWatchWidgets/CoachWatchWidgetsBundle.swift` suffit, Xcode
le répercute. Bonne surprise : notre fichier déclare `struct
CoachWatchWidgetsBundle`, le nom exact du template — rien à renommer.
6. **Étape sans objet sur Xcode récent.** Les nouvelles targets sont créées en
**dossier synchronisé** (`PBXFileSystemSynchronizedRootGroup`) : tout fichier
présent dans le dossier de la target en est membre automatiquement, sans être
listé dans `project.pbxproj`. `CoachWatchComplications.swift` y était déjà,
il est donc déjà inclus — d'où son absence du dialogue *Add Files*, et un
`grep CoachWatchComplications project.pbxproj` qui renvoie `0` **sans que ce
soit un problème**. Vérifier plutôt : `grep -c
PBXFileSystemSynchronizedRootGroup project.pbxproj` ≥ 1.
7. `CoachWidgetSnapshot.swift` vit dans `ios/App/App/`, **hors** du dossier
synchronisé : lui doit être coché à la main. `Cmd+Shift+O` → tape son nom →
`Cmd+Option+1`**Target Membership** → coche **CoachWatchWidgets** sans
décocher le reste. Sans lui : `cannot find 'CoachWidgetStore' in scope`.
8. Target `CoachWatchWidgets`**Signing & Capabilities****+ Capability** →
**App Groups** → coche `group.ch.hypnotruck.coach`
9. ⚠️ **Aligner le numéro de build.** Xcode crée la target en build `1` alors que
le projet est en `22``The CFBundleVersion of an app extension ('1') must
match that of its containing parent app ('22')`. Target → **General**
*Identity***Version** `1.2`, **Build** `22`. Contrôle :
`grep -n CURRENT_PROJECT_VERSION project.pbxproj | grep -v "= 22;"` ne doit
rien renvoyer.
Puis commit + push (même raison qu'à l'étape 4).
### Vérifier l'assemblage sans cliquer partout
`Cmd+S` d'abord (Xcode n'écrit le projet sur disque qu'à l'enregistrement) :
```bash
# à quelle target appartient la phase qui embarque l'extension ?
python3 - <<'PY'
import re, pathlib
s = pathlib.Path('ios/App/App.xcodeproj/project.pbxproj').read_text()
emb = dict(re.findall(r'(\w+) /\* (Embed \w[\w ]*) \*/ = \{', s))
for m in re.finditer(r'/\* ([\w.-]+) \*/ = \{\s*isa = PBXNativeTarget;(.*?)\n\t\t\};', s, re.S):
for pid, pname in emb.items():
if pid in m.group(2):
print(f"{pname:<32} -> target {m.group(1)}")
PY
```
`Embed Foundation Extensions -> target CoachWatch` : l'extension part bien sur
la montre. Si elle est rattachée à `App`, elle reste sur l'iPhone et aucune
complication n'apparaîtra jamais.
⚠️ **La Watch n'a pas besoin d'apparaître comme destination dans Xcode.** La
phase *Embed Watch Content* embarque `CoachWatch.app` dans l'app iPhone : builder
le schéma **App** sur l'iPhone installe l'app Watch et son extension. Pour voir
la montre comme destination il faudrait activer le **Mode développeur** dessus
(Réglages → Confidentialité et sécurité), ce qui n'est pas nécessaire ici.
## Étape 6 — Compiler et tester sur l'iPhone
1. Branche l'iPhone, déverrouille-le, avec l'Apple Watch au poignet et appairée