Harden watch sync against schema mismatches and surface log-row settings

The watch now re-applies the phone's application context once activation
completes (real hardware activates asynchronously, so the eager launch
read sees an empty context), and a state push that fails to decode —
a phone/watch build running different document schemas — is logged and
skipped instead of pruning the cache against a bogus empty set. Workout
log rows offer the machine-settings editor for any machine-based library
exercise, not just logs that already carry settings.

Claude-Session: https://claude.ai/code/session_01LEoff8bXGBS83tK1c55Mf7
This commit is contained in:
2026-07-06 20:10:36 -04:00
parent 669ecf1259
commit 6521de8f17
3 changed files with 53 additions and 12 deletions
+8 -4
View File
@@ -39,14 +39,18 @@ enum WCPayload {
return dict
}
static func decodeSplits(_ dict: [String: Any]) -> [SplitDocument] {
/// `nil` means the payload carried splits that failed to decode (a schema mismatch
/// between the two builds) distinct from an absent key or a legitimately empty
/// list, so the receiver can surface it instead of silently applying nothing.
static func decodeSplits(_ dict: [String: Any]) -> [SplitDocument]? {
guard let data = dict[splitsKey] as? Data else { return [] }
return (try? DocumentCoder.decoder.decode([SplitDocument].self, from: data)) ?? []
return try? DocumentCoder.decoder.decode([SplitDocument].self, from: data)
}
static func decodeWorkouts(_ dict: [String: Any]) -> [WorkoutDocument] {
/// See `decodeSplits` `nil` is a decode failure, not an empty list.
static func decodeWorkouts(_ dict: [String: Any]) -> [WorkoutDocument]? {
guard let data = dict[workoutsKey] as? Data else { return [] }
return (try? DocumentCoder.decoder.decode([WorkoutDocument].self, from: data)) ?? []
return try? DocumentCoder.decoder.decode([WorkoutDocument].self, from: data)
}
static func decodeRestSeconds(_ dict: [String: Any]) -> Int? { dict[restSecondsKey] as? Int }