feat(healthkit): deleteWorkout dans CoachHealthRoute (supprime workout d'Apple Santé)

Nouveau plugin method @objc deleteWorkout(workoutUUID:) -> retrouve le HKWorkout
par UUID puis HKHealthStore.delete. requestAuthorization étendu pour inclure
share auth de HKWorkoutType (requise pour delete). Utilisé par la page /workouts
au swipe-to-delete pour synchroniser la suppression avec Apple Santé.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sylvain
2026-05-26 20:07:17 +02:00
parent 4484b3ea47
commit fac7d95cd1

View File

@@ -25,6 +25,7 @@ public class CoachHealthRoutePlugin: CAPPlugin, CAPBridgedPlugin {
CAPPluginMethod(name: "requestAuthorization", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "requestAuthorization", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "getRoute", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getRoute", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "getCharacteristics", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getCharacteristics", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "deleteWorkout", returnType: CAPPluginReturnPromise),
] ]
private lazy var store = HKHealthStore() private lazy var store = HKHealthStore()
@@ -51,7 +52,10 @@ public class CoachHealthRoutePlugin: CAPPlugin, CAPBridgedPlugin {
if let dob = HKObjectType.characteristicType(forIdentifier: .dateOfBirth) { types.insert(dob) } if let dob = HKObjectType.characteristicType(forIdentifier: .dateOfBirth) { types.insert(dob) }
if let sex = HKObjectType.characteristicType(forIdentifier: .biologicalSex) { types.insert(sex) } if let sex = HKObjectType.characteristicType(forIdentifier: .biologicalSex) { types.insert(sex) }
if let blood = HKObjectType.characteristicType(forIdentifier: .bloodType) { types.insert(blood) } if let blood = HKObjectType.characteristicType(forIdentifier: .bloodType) { types.insert(blood) }
store.requestAuthorization(toShare: nil, read: types) { ok, err in // Share workoutType requis pour deleteWorkout (HKHealthStore.delete
// exige share auth). Read-only pour les autres types.
let shareTypes: Set<HKSampleType> = [HKObjectType.workoutType()]
store.requestAuthorization(toShare: shareTypes, read: types) { ok, err in
if let err = err { if let err = err {
call.resolve(["granted": false, "reason": err.localizedDescription]) call.resolve(["granted": false, "reason": err.localizedDescription])
} else { } else {
@@ -201,4 +205,46 @@ public class CoachHealthRoutePlugin: CAPPlugin, CAPBridgedPlugin {
call.resolve(["available": true, "route": payload, "count": payload.count]) call.resolve(["available": true, "route": payload, "count": payload.count])
} }
} }
/// Supprime un workout d'Apple Santé via son UUID. Requiert l'autorisation
/// share pour HKWorkoutType (demandée dans requestAuthorization). Renvoie
/// {deleted: true} si trouvé+supprimé, {deleted: false, reason: ...} si
/// introuvable, ou reject() si erreur HealthKit.
@objc func deleteWorkout(_ call: CAPPluginCall) {
guard HKHealthStore.isHealthDataAvailable() else {
call.reject("HealthKit unavailable")
return
}
guard let uuidStr = call.getString("workoutUUID"),
let uuid = UUID(uuidString: uuidStr) else {
call.reject("workoutUUID requis (UUID string)")
return
}
// 1. Retrouver le HKWorkout via son UUID (même pattern que getRoute).
let pred = HKQuery.predicateForObject(with: uuid)
let wq = HKSampleQuery(
sampleType: HKObjectType.workoutType(),
predicate: pred,
limit: 1,
sortDescriptors: nil
) { _, results, err in
if let err = err {
call.reject("Workout fetch failed: \(err.localizedDescription)")
return
}
guard let workout = results?.first as? HKWorkout else {
call.resolve(["deleted": false, "reason": "workout not found"])
return
}
// 2. Supprime le workout d'Apple Santé (requiert share auth pour workoutType).
self.store.delete(workout) { success, err in
if let err = err {
call.reject("Delete failed: \(err.localizedDescription)")
return
}
call.resolve(["deleted": success])
}
}
store.execute(wq)
}
} }