fix(watch): transmet les blocs de routine, pas seulement l'état coché
pushToWatch ne lisait que `date` et `done` dans l'appel JS et ignorait `blocks`. La montre recevait donc un snapshot sans aucun bloc et restait sur son écran vide « Ouvre l'app Coach sur l'iPhone » — alors que le log confirmait que l'envoi partait bien (constaté sur device le 2026-08-03). sanitizeBlocks ne garde que les clés attendues en types property-list stricts : updateApplicationContext lève une exception ObjC sur tout ce qui ne l'est pas, et les entiers venus du JS arrivent en NSNumber — un `as? Int` direct échoue quand JavaScriptCore les passe en double (même piège que le score perdu dans CoachWidgetBridge). Le log distingue désormais blocs transmis et blocs faits : « 0 blocs » était ambigu et désignait le compte des cochés, pas celui des blocs envoyés. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,10 +54,14 @@ public class CoachRoutineBridgePlugin: CAPPlugin, CAPBridgedPlugin {
|
|||||||
|
|
||||||
// MARK: - JS → natif
|
// MARK: - JS → natif
|
||||||
|
|
||||||
/// `pushToWatch({ date: "2026-08-03", done: ["hanches", ...] })`
|
/// `pushToWatch({ date, done: [...], blocks: [{id, num, title, duration_min, moment}] })`
|
||||||
|
///
|
||||||
|
/// Les blocs voyagent avec l'état : la montre n'a aucune liste codée en dur,
|
||||||
|
/// modifier la routine côté web ne demande donc pas de rebuild watchOS.
|
||||||
@objc func pushToWatch(_ call: CAPPluginCall) {
|
@objc func pushToWatch(_ call: CAPPluginCall) {
|
||||||
let date = call.getString("date") ?? Self.todayISO()
|
let date = call.getString("date") ?? Self.todayISO()
|
||||||
let done = call.getArray("done", String.self) ?? []
|
let done = call.getArray("done", String.self) ?? []
|
||||||
|
let blocks = Self.sanitizeBlocks(call.getArray("blocks") as? [[String: Any]] ?? [])
|
||||||
|
|
||||||
guard WCSession.isSupported() else {
|
guard WCSession.isSupported() else {
|
||||||
call.resolve(["pushed": false, "reason": "WCSession non supporté"])
|
call.resolve(["pushed": false, "reason": "WCSession non supporté"])
|
||||||
@@ -75,10 +79,10 @@ public class CoachRoutineBridgePlugin: CAPPlugin, CAPBridgedPlugin {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
try session.updateApplicationContext([
|
try session.updateApplicationContext([
|
||||||
"routineSnapshot": ["date": date, "done": done]
|
"routineSnapshot": ["date": date, "done": done, "blocks": blocks]
|
||||||
])
|
])
|
||||||
routineLog.info("routine poussée vers la Watch (\(done.count, privacy: .public) blocs)")
|
routineLog.info("routine poussée vers la Watch : \(blocks.count, privacy: .public) blocs, \(done.count, privacy: .public) faits")
|
||||||
call.resolve(["pushed": true, "count": done.count])
|
call.resolve(["pushed": true, "blocks": blocks.count, "done": done.count])
|
||||||
} catch {
|
} catch {
|
||||||
routineLog.error("updateApplicationContext a échoué : \(error.localizedDescription, privacy: .public)")
|
routineLog.error("updateApplicationContext a échoué : \(error.localizedDescription, privacy: .public)")
|
||||||
call.reject("Envoi vers la Watch impossible : \(error.localizedDescription)")
|
call.reject("Envoi vers la Watch impossible : \(error.localizedDescription)")
|
||||||
@@ -151,6 +155,23 @@ public class CoachRoutineBridgePlugin: CAPPlugin, CAPBridgedPlugin {
|
|||||||
}.resume()
|
}.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ne garde que les clés attendues, en types property-list stricts.
|
||||||
|
///
|
||||||
|
/// Deux raisons : `updateApplicationContext` rejette (exception ObjC) tout
|
||||||
|
/// ce qui n'est pas plist-compatible, et les entiers venus du JS arrivent en
|
||||||
|
/// `NSNumber` — un `as? Int` direct échoue quand JavaScriptCore les a passés
|
||||||
|
/// en double. Même piège que le score perdu dans `CoachWidgetBridge`.
|
||||||
|
private static func sanitizeBlocks(_ raw: [[String: Any]]) -> [[String: Any]] {
|
||||||
|
raw.compactMap { b in
|
||||||
|
guard let id = b["id"] as? String, let title = b["title"] as? String else { return nil }
|
||||||
|
var out: [String: Any] = ["id": id, "title": title]
|
||||||
|
out["num"] = (b["num"] as? NSNumber)?.intValue ?? 0
|
||||||
|
out["duration_min"] = (b["duration_min"] as? NSNumber)?.intValue ?? 0
|
||||||
|
out["moment"] = (b["moment"] as? String) ?? "matin"
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func todayISO() -> String {
|
private static func todayISO() -> String {
|
||||||
let fmt = DateFormatter()
|
let fmt = DateFormatter()
|
||||||
fmt.dateFormat = "yyyy-MM-dd"
|
fmt.dateFormat = "yyyy-MM-dd"
|
||||||
|
|||||||
Reference in New Issue
Block a user