WorkoutKit Swift : sendInterval accepte blockSteps[] (N steps custom)
Avant : work + rest fixes (2 steps) avec repeats sur le couple.
Maintenant : payload optionnel blockSteps = [{type, duration_min, hr_zone},
...] qui permet N steps custom dans le bloc à répéter. Backward compat :
si pas de blockSteps, lit work + rest comme avant.
Refactor de sendCustomWorkout : prend stepsSpec: [(min, hrZone, isWork)]
au lieu de work*/rest* params séparés. Génère N IntervalSteps depuis le
spec, met dans 1 IntervalBlock avec iterations=repeats.
This commit is contained in:
@@ -60,13 +60,37 @@ public class CoachWorkoutKitPlugin: CAPPlugin, CAPBridgedPlugin {
|
|||||||
let cooldownMin = call.getDouble("cooldownMin") ?? 10
|
let cooldownMin = call.getDouble("cooldownMin") ?? 10
|
||||||
let repeats = max(1, min(50, call.getInt("repeats") ?? 6))
|
let repeats = max(1, min(50, call.getInt("repeats") ?? 6))
|
||||||
|
|
||||||
|
// Nouveau format : blockSteps = [{ type, duration_min, hr_zone }, ...]
|
||||||
|
// type = "work" | "recovery"
|
||||||
|
// Backward compat : si pas blockSteps, lit work + rest comme avant.
|
||||||
|
var stepsSpec: [(min: Double, hrZone: Int?, isWork: Bool)] = []
|
||||||
|
if let raw = call.getArray("blockSteps") as? [[String: Any]], !raw.isEmpty {
|
||||||
|
for (i, s) in raw.enumerated() {
|
||||||
|
let durMin = (s["duration_min"] as? Double) ?? 0
|
||||||
|
if durMin <= 0 { continue }
|
||||||
|
let hrZone = s["hr_zone"] as? Int
|
||||||
|
let typeStr = (s["type"] as? String) ?? (i % 2 == 0 ? "work" : "recovery")
|
||||||
|
stepsSpec.append((durMin, hrZone, typeStr == "work"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Backward compat — work + rest (rest skipped si 0)
|
||||||
let workDict = call.getObject("work") ?? [:]
|
let workDict = call.getObject("work") ?? [:]
|
||||||
let workMin = workDict["duration_min"] as? Double ?? 3
|
let workMin = workDict["duration_min"] as? Double ?? 3
|
||||||
let workHrZone = workDict["hr_zone"] as? Int
|
let workHrZone = workDict["hr_zone"] as? Int
|
||||||
|
stepsSpec.append((workMin, workHrZone, true))
|
||||||
|
|
||||||
let restDict = call.getObject("rest") ?? [:]
|
let restDict = call.getObject("rest") ?? [:]
|
||||||
let restMin = restDict["duration_min"] as? Double ?? 1.5
|
let restMin = restDict["duration_min"] as? Double ?? 1.5
|
||||||
let restHrZone = restDict["hr_zone"] as? Int
|
let restHrZone = restDict["hr_zone"] as? Int
|
||||||
|
if restMin > 0 {
|
||||||
|
stepsSpec.append((restMin, restHrZone, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if stepsSpec.isEmpty {
|
||||||
|
call.reject("Au moins 1 step (durée > 0) requis dans le bloc")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let activity = workoutActivity(from: activityStr)
|
let activity = workoutActivity(from: activityStr)
|
||||||
|
|
||||||
@@ -76,10 +100,7 @@ public class CoachWorkoutKitPlugin: CAPPlugin, CAPBridgedPlugin {
|
|||||||
activity: activity,
|
activity: activity,
|
||||||
displayName: displayName,
|
displayName: displayName,
|
||||||
warmupMin: warmupMin,
|
warmupMin: warmupMin,
|
||||||
workMin: workMin,
|
stepsSpec: stepsSpec,
|
||||||
workHrZone: workHrZone,
|
|
||||||
restMin: restMin,
|
|
||||||
restHrZone: restHrZone,
|
|
||||||
repeats: repeats,
|
repeats: repeats,
|
||||||
cooldownMin: cooldownMin
|
cooldownMin: cooldownMin
|
||||||
)
|
)
|
||||||
@@ -119,10 +140,7 @@ public class CoachWorkoutKitPlugin: CAPPlugin, CAPBridgedPlugin {
|
|||||||
activity: HKWorkoutActivityType,
|
activity: HKWorkoutActivityType,
|
||||||
displayName: String,
|
displayName: String,
|
||||||
warmupMin: Double,
|
warmupMin: Double,
|
||||||
workMin: Double,
|
stepsSpec: [(min: Double, hrZone: Int?, isWork: Bool)],
|
||||||
workHrZone: Int?,
|
|
||||||
restMin: Double,
|
|
||||||
restHrZone: Int?,
|
|
||||||
repeats: Int,
|
repeats: Int,
|
||||||
cooldownMin: Double
|
cooldownMin: Double
|
||||||
) async throws {
|
) async throws {
|
||||||
@@ -130,24 +148,21 @@ public class CoachWorkoutKitPlugin: CAPPlugin, CAPBridgedPlugin {
|
|||||||
// 1. Warmup — un WorkoutStep avec un goal time
|
// 1. Warmup — un WorkoutStep avec un goal time
|
||||||
let warmupStep = WorkoutStep(goal: .time(warmupMin * 60, .seconds))
|
let warmupStep = WorkoutStep(goal: .time(warmupMin * 60, .seconds))
|
||||||
|
|
||||||
// 2. Work step — wrapped dans un IntervalStep avec purpose .work
|
// 2. Construit la liste d'IntervalSteps depuis stepsSpec.
|
||||||
var workWorkoutStep = WorkoutStep(goal: .time(workMin * 60, .seconds))
|
// Chaque entrée = (durée min, zone HR optionnelle, isWork bool).
|
||||||
if let zone = workHrZone {
|
let intervalSteps: [IntervalStep] = stepsSpec.map { spec in
|
||||||
workWorkoutStep.alert = HeartRateZoneAlert(zone: zone)
|
var ws = WorkoutStep(goal: .time(spec.min * 60, .seconds))
|
||||||
|
if let zone = spec.hrZone {
|
||||||
|
ws.alert = HeartRateZoneAlert(zone: zone)
|
||||||
}
|
}
|
||||||
let workInterval = IntervalStep(.work, step: workWorkoutStep)
|
return IntervalStep(spec.isWork ? .work : .recovery, step: ws)
|
||||||
|
|
||||||
// 3. Rest step — IntervalStep purpose .recovery
|
|
||||||
var restWorkoutStep = WorkoutStep(goal: .time(restMin * 60, .seconds))
|
|
||||||
if let zone = restHrZone {
|
|
||||||
restWorkoutStep.alert = HeartRateZoneAlert(zone: zone)
|
|
||||||
}
|
}
|
||||||
let restInterval = IntervalStep(.recovery, step: restWorkoutStep)
|
|
||||||
|
|
||||||
// 4. Block répété N fois
|
// 3. Block répété N fois — la séquence COMPLÈTE de tous les steps
|
||||||
let block = IntervalBlock(steps: [workInterval, restInterval], iterations: repeats)
|
// se répète repeats fois.
|
||||||
|
let block = IntervalBlock(steps: intervalSteps, iterations: repeats)
|
||||||
|
|
||||||
// 5. Cooldown
|
// 4. Cooldown
|
||||||
let cooldownStep = WorkoutStep(goal: .time(cooldownMin * 60, .seconds))
|
let cooldownStep = WorkoutStep(goal: .time(cooldownMin * 60, .seconds))
|
||||||
|
|
||||||
// 6. CustomWorkout — location selon l'activité (indoor pour
|
// 6. CustomWorkout — location selon l'activité (indoor pour
|
||||||
|
|||||||
Reference in New Issue
Block a user