Files
workouts/Workouts Watch App/WatchAppDelegate.swift
T
rzen abb223daca Make the watch own its workout-session lifecycle
The HKWorkoutSession could only ever be born via the phone's one-shot
startWatchApp handoff (BULLETPROOFING.md H1-H3): a dropped handoff, a
watch crash/reboot, or a run engaged manually on the wrist left the
whole workout sessionless - no heart rate, no Health save, app
suspending wrist-down - and "End Current & Start New" swallowed the
handoff against the old session's idempotency guard.

Now the coordinator self-starts a session on any reconcile that finds
an active run with none running (SessionEndPlanner.shouldStart /
runToStart, activity type from the run's split), recover() re-adopts a
crash-orphaned session at launch, and a system-ended session salvages
its Health save instead of dropping it. A .finish decided for a
session younger than 30s demotes to .discard so the stale-context
races can't save junk workouts attributed to the wrong run; parallel
completions now pick the survivor deterministically.

Claude-Session: https://claude.ai/code/session_01PVNBVKp5bcq52X722uMjwT
2026-07-09 22:57:26 -04:00

37 lines
1.3 KiB
Swift

//
// WatchAppDelegate.swift
// Workouts Watch App
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import WatchKit
import HealthKit
/// Bridges watchOS lifecycle into the workout session. When the phone starts a workout
/// it calls `startWatchApp(toHandle:)`; watchOS launches (or foregrounds) this app and
/// delivers the configuration to `handle(_:)`, where we start a session to claim
/// foreground runtime. The session manager is shared with the view tree via the
/// environment (see `WorkoutsWatchApp`).
@MainActor
final class WatchAppDelegate: NSObject, WKApplicationDelegate {
let sessionManager = WorkoutSessionManager()
func applicationDidFinishLaunching() {
#if DEBUG
// The screenshot harness renders fixed screens — don't pop the Health auth
// dialog over them (it would also leak into App Store captures).
if ScreenshotSeed.isActive { return }
#endif
sessionManager.requestAuthorization()
// Re-adopt a session that outlived a crash or reboot — watchOS relaunches us
// expecting this call; without it the session is orphaned and its workout lost.
sessionManager.recover()
}
func handle(_ workoutConfiguration: HKWorkoutConfiguration) {
sessionManager.start(with: workoutConfiguration)
}
}