This commit is contained in:
2025-10-10 16:32:10 -06:00
parent 719181aa16
commit 40efd6636f
9 changed files with 159 additions and 38 deletions

View File

@@ -15,9 +15,12 @@ class HealthKitService: ObservableObject {
private let userDefaults = UserDefaults.standard
private let isEnabledKey = "healthKitEnabled"
private let workoutStartDateKey = "healthKitWorkoutStartDate"
private let workoutSessionIdKey = "healthKitWorkoutSessionId"
private init() {
loadSettings()
restoreActiveWorkout()
}
func loadSettings() {
@@ -28,6 +31,62 @@ class HealthKitService: ObservableObject {
}
}
/// Restore active workout state
private func restoreActiveWorkout() {
if let startDate = userDefaults.object(forKey: workoutStartDateKey) as? Date,
let sessionIdString = userDefaults.string(forKey: workoutSessionIdKey),
let sessionId = UUID(uuidString: sessionIdString)
{
currentWorkoutStartDate = startDate
currentWorkoutSessionId = sessionId
print("HealthKit: Restored active workout from \(startDate)")
}
}
/// Persist active workout state
private func persistActiveWorkout() {
if let startDate = currentWorkoutStartDate, let sessionId = currentWorkoutSessionId {
userDefaults.set(startDate, forKey: workoutStartDateKey)
userDefaults.set(sessionId.uuidString, forKey: workoutSessionIdKey)
} else {
userDefaults.removeObject(forKey: workoutStartDateKey)
userDefaults.removeObject(forKey: workoutSessionIdKey)
}
}
/// Verify and restore health integration
func verifyAndRestoreIntegration() async {
guard isEnabled else { return }
guard HKHealthStore.isHealthDataAvailable() else {
print("HealthKit: Device does not support HealthKit")
return
}
checkAuthorization()
if !isAuthorized {
print(
"HealthKit: Integration was enabled but authorization lost, attempting to restore..."
)
do {
try await requestAuthorization()
print("HealthKit: Authorization restored successfully")
} catch {
print("HealthKit: Failed to restore authorization: \(error.localizedDescription)")
}
} else {
print("HealthKit: Integration verified - authorization is valid")
}
if hasActiveWorkout() {
print(
"HealthKit: Active workout restored - started at \(currentWorkoutStartDate!)"
)
}
}
func setEnabled(_ enabled: Bool) {
isEnabled = enabled
userDefaults.set(enabled, forKey: isEnabledKey)
@@ -73,6 +132,8 @@ class HealthKitService: ObservableObject {
currentWorkoutStartDate = startDate
currentWorkoutSessionId = sessionId
persistActiveWorkout()
print("HealthKit: Started workout for session \(sessionId)")
}
func endWorkout(endDate: Date) async throws {
@@ -93,26 +154,45 @@ class HealthKitService: ObservableObject {
let energyBurned = HKQuantity(unit: .kilocalorie(), doubleValue: calories)
let workout = HKWorkout(
activityType: .climbing,
start: startDate,
end: endDate,
duration: duration,
totalEnergyBurned: energyBurned,
totalDistance: nil,
metadata: [
HKMetadataKeyIndoorWorkout: true
]
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.activityType = .climbing
workoutConfiguration.locationType = .indoor
let builder = HKWorkoutBuilder(
healthStore: healthStore,
configuration: workoutConfiguration,
device: .local()
)
do {
try await healthStore.save(workout)
try await builder.beginCollection(at: startDate)
let energyBurnedType = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)!
let energySample = HKQuantitySample(
type: energyBurnedType,
quantity: energyBurned,
start: startDate,
end: endDate
)
try await builder.addSamples([energySample])
try await builder.addMetadata([HKMetadataKeyIndoorWorkout: true])
try await builder.endCollection(at: endDate)
let workout = try await builder.finishWorkout()
print(
"HealthKit: Workout saved successfully with id: \(workout?.uuid.uuidString ?? "unknown")"
)
currentWorkoutStartDate = nil
currentWorkoutSessionId = nil
persistActiveWorkout()
} catch {
print("HealthKit: Failed to save workout: \(error.localizedDescription)")
currentWorkoutStartDate = nil
currentWorkoutSessionId = nil
persistActiveWorkout()
throw HealthKitError.workoutSaveFailed
}
@@ -121,6 +201,8 @@ class HealthKitService: ObservableObject {
func cancelWorkout() {
currentWorkoutStartDate = nil
currentWorkoutSessionId = nil
persistActiveWorkout()
print("HealthKit: Workout cancelled")
}
func hasActiveWorkout() -> Bool {