Le détail natif expose les activités HealthKit de la séance, en lecture seule

À la demande explicite de Sylvain (22/09), après la perte des pas fins
côté montre. Apple documente que les HKWorkoutActivity peuvent
« represent the active and rest periods during interval training », mais
ne dit pas que l'app Exercice en écrit une par étape d'un exercice
personnalisé. Ce champ `activities` (début, fin, durée, type, nombre
d'événements, métadonnées en clair) permet de le constater dans un brut.

Le serveur ne s'en sert pas : rien n'est stocké ni affiché tant que la
donnée n'a pas été vue. Syntaxe vérifiée (swiftc -parse), build Xcode à
faire sur le Mac mini.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Sylvain Bettinelli
2026-09-22 19:32:40 +00:00
parent 31467851d9
commit b15867273c

View File

@@ -423,6 +423,38 @@ public class CoachHealthRoutePlugin: CAPPlugin, CAPBridgedPlugin {
out["average_mets"] = (mets.doubleValue(for: unit) * 10).rounded() / 10 out["average_mets"] = (mets.doubleValue(for: unit) * 10).rounded() / 10
} }
// Activités de la séance — lecture seule, pour VOIR ce qu'Exercice écrit.
// Apple : « you can use workout activities to […] represent the active
// and rest periods during interval training. All HKWorkout instances
// have at least one associated HKWorkoutActivity » (doc HKWorkoutActivity,
// lue le 22/09/2026). Apple ne dit PAS que l'app Exercice en écrit une
// par étape d'un exercice personnalisé : c'est ce que ce champ permet
// de constater. Le serveur ne s'en sert pas tant que ce n'est pas vu.
if #available(iOS 16.0, *) {
let iso = ISO8601DateFormatter()
iso.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
var activities: [[String: Any]] = []
for a in workout.workoutActivities {
var row: [String: Any] = [
"start": iso.string(from: a.startDate),
"duration_s": (a.duration * 10).rounded() / 10,
"activity_type": Int(a.workoutConfiguration.activityType.rawValue),
"location": Int(a.workoutConfiguration.locationType.rawValue),
"events": a.workoutEvents.count,
]
if let end = a.endDate { row["end"] = iso.string(from: end) }
// Métadonnées en clair uniquement (String/Number/Bool) : une
// HKQuantity ne se sérialise pas, et on ne veut rien deviner.
var plain: [String: Any] = [:]
for (k, v) in a.metadata ?? [:] {
if v is String || v is NSNumber { plain[k] = v }
}
if !plain.isEmpty { row["metadata"] = plain }
activities.append(row)
}
out["activities"] = activities
}
// Statistiques agrégées sur la fenêtre exacte de la séance. // Statistiques agrégées sur la fenêtre exacte de la séance.
let samplePredicate = HKQuery.predicateForObjects(from: workout) let samplePredicate = HKQuery.predicateForObjects(from: workout)
var requests: [(key: String, id: HKQuantityTypeIdentifier, unit: HKUnit, option: HKStatisticsOptions, scale: Double)] = [] var requests: [(key: String, id: HKQuantityTypeIdentifier, unit: HKUnit, option: HKStatisticsOptions, scale: Double)] = []