Workout: restore history/missed/compensations from iCloud (KC-39)

save() pushed all 7 workout keys to iCloud KVS, but init only restored 4 —
workout history, missed dates, and rest-day compensations were uploaded yet
never restored, so they were lost on reinstall/update. Added the three missing
restoreIfMissing calls so restore mirrors save.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kutesir
2026-06-24 21:52:16 +03:00
parent b6c9f5b0ca
commit 09acf7afa5
2 changed files with 35 additions and 1 deletions

View File

@@ -1215,3 +1215,33 @@ Added DEBUG-only test hooks (`KISANI_AUTOADD`, `KISANI_VOICE_SIM`) to inject a
transcript on the simulator; `#if DEBUG`, compiled out of Release.
Files: `TodayView.swift` (`NLTaskField.updateUIView`), `ContentView.swift`.
---
## KC-39 — Workout state lost on reinstall/update (incomplete iCloud restore)
**Status:** Fixed (build-verified)
**Reported by:** User
**Area:** Workout / iCloud sync
### Description
Workout state was lost after app updates / fresh installs.
### Root cause
`save()` pushes 7 workout keys to iCloud KVS (programs, schedule, activePid,
completedDates, history, missedDates, compensations), but `WorkoutViewModel.init`
only `restoreIfMissing(...)` for the first four. So **history, missed dates, and
rest-day compensations** were uploaded to iCloud but never restored on a fresh
launch — silently lost on reinstall/update.
### Fix
Added the three missing `restoreIfMissing` calls (`history`, `missedDates`,
`compensations`) so restore is symmetric with save. (Per-day `lastActiveDay` is
transient set-reset state and intentionally not synced.)
Files: `ExerciseModels.swift`.
### Note
Cross-device *live* refresh while the app is open isn't wired (`WorkoutViewModel`
doesn't observe `kisaniCloudDataRefreshed`) — out of scope; the restore-on-launch
path is what preserves state across updates.

View File

@@ -290,11 +290,15 @@ class WorkoutViewModel: ObservableObject {
self.kMissedDates = "kisani.workout.missedDates.\(uid)"
self.kCompensations = "kisani.workout.compensations.\(uid)"
// Restore all workout keys from iCloud if missing locally (fresh install / new build)
// Restore all workout keys from iCloud if missing locally (fresh install / new build).
// Must cover EVERY key save() pushes, or that state is lost on reinstall/update.
CloudSyncManager.shared.restoreIfMissing(key: "kisani.workout.programs.\(uid)")
CloudSyncManager.shared.restoreIfMissing(key: "kisani.workout.schedule.\(uid)")
CloudSyncManager.shared.restoreIfMissing(key: "kisani.workout.activePid.\(uid)")
CloudSyncManager.shared.restoreIfMissing(key: "kisani.workout.completedDates.\(uid)")
CloudSyncManager.shared.restoreIfMissing(key: "kisani.workout.history.\(uid)")
CloudSyncManager.shared.restoreIfMissing(key: "kisani.workout.missedDates.\(uid)")
CloudSyncManager.shared.restoreIfMissing(key: "kisani.workout.compensations.\(uid)")
// Attempt to restore persisted state
if let data = UserDefaults.kisani.data(forKey: kPrograms),