From 089b6fbde6e00918cb701d362378a64cc1456394 Mon Sep 17 00:00:00 2001 From: Sylvain Bettinelli Date: Tue, 11 Aug 2026 14:34:28 +0000 Subject: [PATCH] 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) --- docs/SESSION-XCODE.md | 67 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/docs/SESSION-XCODE.md b/docs/SESSION-XCODE.md index dd35c9c..65ac239 100644 --- a/docs/SESSION-XCODE.md +++ b/docs/SESSION-XCODE.md @@ -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