Files
coach-ios/CLAUDE.md
Sylvain Bettinelli 1d2d7a0e18 Une toolchain Swift sur Linux, pour ne plus ecrire de dates a l'aveugle
J'ai ecrit `asOf(_:)` hier sans pouvoir le compiler. Swift 6.3 est desormais
installe sur la machine de dev (Debian 12 x86_64) et `tests-linux/` compile
et teste la logique PURE des widgets.

10 tests, dont 5 verifies rouges en neutralisant la peremption. Ils couvrent
la remise a zero, la conservation des objectifs et des zones, le report de la
seance du lendemain, et le passage a l'heure d'hiver du 25 octobre — ou un
`+86400` naif ferait basculer le widget une heure trop tot, la veille au soir.

Les sources du paquet sont des liens symboliques vers les vrais fichiers : le
test porte sur le code livre, pas sur une copie.

⚠️ Ne remplace PAS le build Xcode et ne le remplacera jamais : WidgetKit,
SwiftUI, HealthKit et WatchKit sont absents de Swift pour Linux. Ni l'app, ni
les widgets, ni les vues ne se compilent ici.
2026-08-18 07:42:59 +00:00

7.5 KiB

CLAUDE.md — coach-ios (wrapper mobile Capacitor)

Instructions pour Claude Code sur ce projet. Lis ce fichier en début de session.

🎯 Contexte projet

  • Nature : wrapper Capacitor 8 (iOS + Android) qui charge https://coach.hypnotruck.ch dans une WebView
  • Pas de code web embarquéserver.url dans capacitor.config.ts pointe vers la prod → déploiement web = mise à jour app sans review Apple/Google
  • Bundle ID : ch.hypnotruck.coach (iOS + Android)
  • Repo Gitea : encore nommé coach-ios (renommage coach-mobile à faire) ; fichier coach-ios.local.json conservé pour ne pas casser les refs Swift
  • Plateformes : iOS 13+ et Android 8.0+ (API 26+, requis par Health Connect)
  • Build iOS : nécessite le Mac mini (macOS 13+, Xcode 15+). Pas de Mac cloud nécessaire.
  • État : iOS prêt (HealthKit + cookie inject AppDelegate). Android prêt. Bloqué externe : upgrade macOS 26 pour build iOS + Play Console $25 ~3-5j.

🔒 Règles non négociables

  1. Le code web n'est PAS ici — pour modifier l'UI/UX, c'est coach_sportif/web/. Ce repo ne touche QUE le natif (Swift, Kotlin, plugins Capacitor, config).
  2. Cookie inject AppDelegate (iOS) : mécanisme critique pour l'auth WKWebView. Toute modif AppDelegate.swift doit préserver ce flux.
  3. Health Connect Android : nécessite la privacy policy URL dans le manifest (validation Play Console).
  4. Pas d'extrapolation : root cause vérifiable avant tout fix natif. iOS+Android = doubles surfaces de bug.
  5. Default yes : décider et exécuter (sauf actions destructives).
  6. Pousser après commit : git push systématique.
  7. Descriptions Info.plist en FR (NSHealthShareUsageDescription etc.) — App Store FR.

🛠️ Workflow par phase

Avant toute feature native

  • brainstorming — explorer l'intent (souvent : "ce truc marche sur web, comment le porter en natif ?")
  • grill-with-docs — challenger : est-ce vraiment du natif ou du web qui marcherait via WebView ?

Code Swift / iOS

  • swiftui-pro — quand le chantier watchOS live workout démarrera (HKWorkoutPlan + WatchConnectivity)
  • Vérifier CoachHealthRoute.swift, CoachWorkoutKit.swift, CoachWorkoutObserver.swift avant de toucher au flow HealthKit

Code Kotlin / Android

  • compose-state-hoisting — si plugin Capacitor custom avec UI Compose
  • kotlin-coroutines-structured-concurrency — appels Health Connect async
  • kotlin-flow-state-event-modeling — observations Health Connect

Bug / régression natif

  • investigate — root cause discipline (cassures fréquentes : permissions, signatures, entitlements)
  • diagnose — reproduire sur device réel, pas juste simulator

QA / ship

  • qa (gstack) — l'app pointant vers la prod web, tester le web couvre 80%
  • webapp-testing — Playwright sur coach.hypnotruck.ch (le contenu réel de la WebView)
  • cso — audit signatures, entitlements, secrets dans coach-ios.local.json
  • ship — workflow PR (mais les releases App Store/Play Store restent manuelles depuis Mac mini)

📂 Conventions code

  • iOS natif : ios/App/App/*.swift
    • AppDelegate.swift — bootstrap + cookie inject (NE PAS CASSER)
    • MainViewController.swift — WKWebView config
    • CoachAuth.swift.example → copier en CoachAuth.swift (gitignored, contient secret partagé)
    • CoachHealthRoute.swift / CoachWorkoutKit.swift / CoachWorkoutObserver.swift — pont HealthKit ↔ web
  • Android natif : android/app/src/main/java/ch/...
  • Config Capacitor : capacitor.config.ts (TS, source de vérité) → généré vers ios/App/App/capacitor.config.json
  • Resources : resources/ → assets (icons, splash) générés par @capacitor/assets

🧪 Tests Swift sur Linux (tests-linux/)

Une toolchain Swift 6.3 est installée sur la machine de dev : /home/coder/workspace/toolchains/bin/swift. Lancer : ./tests-linux/run.sh.

⚠️ Ça ne remplace PAS le build Xcode, et ça ne le remplacera jamais. WidgetKit, SwiftUI, HealthKit, WatchKit et UIKit sont des frameworks Apple, fermés et propres à leurs plateformes : absents de Swift pour Linux. Ni l'app, ni les widgets, ni les vues ne se compilent ici. Pas de xcodebuild, pas de simulateur, pas de signature, pas d'.ipa.

Ce que ça couvre : les fichiers qui n'importent que Foundation — CoachWidgetSnapshot.swift (dont la péremption asOf(_:)) et CoachQuickLog.swift. C'est-à-dire l'arithmétique de dates, là où un décalage d'un jour ne se voit pas à la relecture.

  • Les sources du paquet sont des liens symboliques vers les vrais fichiers : le test porte sur le code livré, pas sur une copie qui dériverait.
  • HeartRateZonesStub.swift est une doublure : le vrai type expose une propriété Color et importe SwiftUI. Elle ne reproduit que les propriétés stockées, et asOf ne fait que les recopier — si la péremption devait un jour TOUCHER aux zones, ce test cesserait d'être pertinent.

⚠️ /home/coder/workspace est le seul chemin persistant du conteneur de dev ; tout le reste (y compris les dépôts) vit sur une couche éphémère. D'où l'emplacement de la toolchain.

🔗 Plugins Capacitor utilisés

  • @capacitor/push-notifications
  • @capacitor/local-notifications
  • @capacitor/geolocation
  • @capgo/capacitor-health — HealthKit (iOS) / Health Connect (Android)
  • @capacitor/splash-screen
  • @capacitor/status-bar
  • @capacitor/haptics

🚧 Backlog notable

  • Renommer le repo Gitea coach-ioscoach-mobile
  • Upgrade macOS 26 sur Mac mini pour reprendre les builds iOS
  • Play Console validation (~3-5j, $25 one-shot) pour upload Android
  • watchOS live workout compagnon — Phases 1-3 RÉALISÉES (2026-05-25, sur main) : target CoachWatch + 4 fichiers Swift watchOS + plugin Capacitor CoachLiveBridge. Builds verts (watchsimulator + App embed), UI validée sur sim. Reste : test device + TestFlight. Détails et deltas watchOS 26 dans docs/watchos-live-workout-plan.md (section "Avancement réel").
  • Push workouts vers Apple Watch (HKWorkoutPlan) / Polar (Training Targets API) / Garmin — backlog 2026-05-12
  • Lancer la séance du jour depuis la complication Watch — proposé et mis de côté le 2026-08-11. État vérifié ce jour :
    • les complications portent déjà un widgetURL (/forme, /calendar), mais CoachWatch n'implémente aucun onOpenURL → un tap ouvre l'app sur ActivityPickerView, pas sur la séance ;
    • la donnée est déjà sur la montre : CoachWidgetSnapshot.TodaySession (sport, titre, durée) y arrive par WCSession, l'app ne s'en sert pas ;
    • travail estimé ~1 h : lire l'URL entrante, mapper TodaySession.sport vers le HKWorkoutActivityType correspondant (les 4 activités de ActivityPickerView), ouvrir l'écran de démarrage pré-rempli.
    • ⚠️ Limite Apple : une complication ne peut PAS ouvrir l'app Exercice d'Apple sur une séance programmée précise — aucune URL publique. Le fractionné structuré envoyé par WorkoutKit se lancera toujours à la main depuis Exercice → Programmées. Guider les intervalles dans notre app Watch serait un chantier d'1 à 2 jours qui doublonnerait l'app d'Apple.

⚠️ Limites connues sync

  • Apple Watch sync : impossible via API tierce — le wrapper HealthKit dans cette app est obligatoire pour récupérer les données Watch.
  • Polar AccessLink : pas d'historique pré-enregistrement (limitation API).
  • Push training-targets vers Polar : nécessiterait migration AccessLink v4 (backlog).