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