feat(workoutkit): whitelist activities + isInspectable WKWebView

Backlog #1 — WKWebView debugging via Safari Web Inspector :
- MainViewController.swift : webView?.isInspectable = true (iOS 16.4+)
- Permet de débugger l'app TestFlight depuis Safari Web Inspector
  Mac (menu Développement → iPhone → coach.hypnotruck.ch). Sans ça,
  la WebView ne remonte pas dans la liste des cibles inspectables.

Backlog #2 — Whitelist défensive activities CustomWorkout :
- Nouvelle fonction isSafeForCustomWorkout(HKWorkoutActivityType) -> Bool
- Whitelist : running, cycling, swimming, walking, hiking, rowing,
  skatingSports, paddleSports (confirmées doc Apple WWDC23+24) +
  functionalStrengthTraining, traditionalStrengthTraining (confirmées
  empiriquement le 2026-05-18 build 15).
- Pour toute activity hors whitelist (yoga, pilates, hiit, crossTraining,
  coreTraining, etc.) : fallback automatique sur .running avec NSLog
  warning. Évite les precondition crashes CustomWorkout.init pour
  activities non testées.
- Retour JS enrichi : { sent, activityUsed (rawValue), activityFallback (bool) }
  pour permettre au web d'afficher un avertissement si fallback.

Bump version requis côté Mac avant rebuild TestFlight.
This commit is contained in:
Sylvain Bettinelli
2026-05-18 10:23:11 +00:00
parent 80567d4611
commit 5a91d0b9be
2 changed files with 43 additions and 2 deletions

View File

@@ -98,7 +98,19 @@ public class CoachWorkoutKitPlugin: CAPPlugin, CAPBridgedPlugin {
return return
} }
let activity = workoutActivity(from: activityStr) let requestedActivity = workoutActivity(from: activityStr)
// Whitelist défensive CustomWorkout.init fait des asserts internes
// (precondition failure non catchable, crash dur) pour les activities
// non supportées. La doc Apple confirme : .running, .cycling toujours,
// .swimming/.rowing/.skating depuis watchOS 11 (WWDC24). Empirique
// (testé 2026-05-18 build 15) : .functionalStrengthTraining marche.
// Pour tout le reste : fallback .running pour éviter crash.
let activity = isSafeForCustomWorkout(requestedActivity)
? requestedActivity
: .running
if activity != requestedActivity {
NSLog("[CoachWorkoutKit] Activity rawValue=%lu not whitelisted for CustomWorkout, falling back to .running", requestedActivity.rawValue)
}
Task { Task {
do { do {
@@ -110,13 +122,34 @@ public class CoachWorkoutKitPlugin: CAPPlugin, CAPBridgedPlugin {
repeats: repeats, repeats: repeats,
cooldownMin: cooldownMin cooldownMin: cooldownMin
) )
call.resolve(["sent": true]) call.resolve([
"sent": true,
"activityUsed": Int(activity.rawValue),
"activityFallback": activity != requestedActivity,
])
} catch { } catch {
call.reject("Failed to send workout: \(error.localizedDescription)") call.reject("Failed to send workout: \(error.localizedDescription)")
} }
} }
} }
// Whitelist des HKWorkoutActivityType compatibles avec CustomWorkout.
// Mettre à jour dès qu'une nouvelle activity est testée empiriquement
// sans crash sur watchOS / iOS courants.
private func isSafeForCustomWorkout(_ activity: HKWorkoutActivityType) -> Bool {
switch activity {
// Confirmé doc Apple WWDC23+24
case .running, .cycling, .swimming, .walking, .hiking,
.rowing, .skatingSports, .paddleSports:
return true
// Confirmé empiriquement 2026-05-18 (build 15)
case .functionalStrengthTraining, .traditionalStrengthTraining:
return true
default:
return false
}
}
private func workoutActivity(from str: String) -> HKWorkoutActivityType { private func workoutActivity(from str: String) -> HKWorkoutActivityType {
switch str.lowercased() { switch str.lowercased() {
// Cardio outdoor // Cardio outdoor

View File

@@ -27,5 +27,13 @@ class MainViewController: CAPBridgeViewController {
webView?.isOpaque = false webView?.isOpaque = false
webView?.backgroundColor = .black webView?.backgroundColor = .black
webView?.scrollView.backgroundColor = .black webView?.scrollView.backgroundColor = .black
// Permet l'inspection de la WKWebView depuis Safari Web Inspector
// (Mac : menu Développement iPhone coach.hypnotruck.ch) y compris
// pour les builds TestFlight. iOS 16.4+ requis. Sans ça, l'app
// n'apparaît pas dans la liste des cibles inspectables.
if #available(iOS 16.4, *) {
webView?.isInspectable = true
}
} }
} }