Make Apple Health workout recording watch-only

The phone no longer writes estimated Health workouts: the watch, which
runs the live session, is the sole recorder. Replaces WorkoutHealthWriter
with a WorkoutHealthDeleter that only removes a legacy phone-estimate
workout when its record is deleted here, drops the MET calorie table and
the phone's write/read Health scopes, and keeps phoneEstimate decodable
for existing documents.

Claude-Session: https://claude.ai/code/session_01HJDQQDA9QdP8zByg43H5v3
This commit is contained in:
2026-07-08 07:56:19 -04:00
parent 06fa52345b
commit a4ed4df756
9 changed files with 102 additions and 261 deletions
+4 -9
View File
@@ -12,7 +12,7 @@ final class AppServices {
let syncEngine: SyncEngine
let watchBridge: PhoneConnectivityBridge
let workoutLauncher = WorkoutLauncher()
let workoutHealthWriter: WorkoutHealthWriter
let workoutHealthDeleter = WorkoutHealthDeleter()
/// Ephemeral live-run state fed by the watch, observed by the mirror UI. Not persisted.
let liveRunState: LiveRunState
@@ -26,10 +26,6 @@ final class AppServices {
let liveRunState = LiveRunState()
self.liveRunState = liveRunState
self.watchBridge = PhoneConnectivityBridge(container: container, syncEngine: syncEngine, liveRunState: liveRunState)
let writer = WorkoutHealthWriter(container: container, syncEngine: syncEngine)
self.workoutHealthWriter = writer
syncEngine.onWorkoutCompleted = { [weak writer] doc in writer?.workoutCompleted(doc) }
syncEngine.onWatchMetricsArrived = { [weak writer] id in writer?.watchMetricsArrived(workoutID: id) }
#if DEBUG
if ScreenshotSeed.isActive { ScreenshotSeed.populate(container.mainContext) }
#endif
@@ -43,10 +39,9 @@ final class AppServices {
guard let self else { return }
await self.syncEngine.connect()
self.watchBridge.activate()
// Past the iCloud gate and cache is reconciled: request Health access and
// back-fill any recently-completed workout that never reached Health.
self.workoutHealthWriter.authorizeIfNeeded()
self.workoutHealthWriter.sweepRecentlyCompleted()
// Past the iCloud gate: request the workout-share scope so the user can still
// delete legacy phone-estimate workouts from Health when deleting them here.
self.workoutHealthDeleter.authorizeIfNeeded()
}
bootstrapTask = task
await task.value