// // HealthManager.swift // FitnessApp // // Created by Matt Bruce on 12/20/24. // import Foundation import HealthKit extension Date { static var startOfDay: Date { let calendar = Calendar.current return calendar.startOfDay(for: Date()) } } class HealthManager { static let shared = HealthManager() let healthStore = HKHealthStore() private init () { Task { do { try await requestHealthKitAccess() } catch { } } } func requestHealthKitAccess() async throws { let calories = HKQuantityType(.activeEnergyBurned) let exercise = HKQuantityType(.appleExerciseTime) let stand = HKCategoryType(.appleStandHour) let healthTypes: Set = [calories, exercise, stand] try await healthStore.requestAuthorization(toShare: [], read: healthTypes) } func fetchTodayCaloriesBurned(completion: @escaping(Result) -> Void) { guard HKHealthStore.isHealthDataAvailable() else { completion(.failure(NSError(domain: "HealthManager", code: -2, userInfo: [NSLocalizedDescriptionKey: "Health data unavailable"]))) return } let calories = HKQuantityType(.activeEnergyBurned) let predicate = HKQuery.predicateForSamples(withStart: .startOfDay, end: Date()) let query = HKStatisticsQuery(quantityType: calories, quantitySamplePredicate: predicate) { _, results, error in if let error = error { completion(.failure(error)) return } if let quantity = results?.sumQuantity() { let caloriesBurned = quantity.doubleValue(for: .kilocalorie()) completion(.success(caloriesBurned)) } else { completion(.failure(NSError(domain: "HealthManager", code: -3, userInfo: [NSLocalizedDescriptionKey: "No data found for today's calories"]))) } } healthStore.execute(query) } func fetchTodayExerciseTime(completion: @escaping(Result) -> Void) { guard HKHealthStore.isHealthDataAvailable() else { completion(.failure(NSError(domain: "HealthManager", code: -2, userInfo: [NSLocalizedDescriptionKey: "Health data unavailable"]))) return } let exercise = HKQuantityType(.appleExerciseTime) let predicate = HKQuery.predicateForSamples(withStart: .startOfDay, end: Date()) let query = HKStatisticsQuery(quantityType: exercise, quantitySamplePredicate: predicate) { _, results, error in if let error = error { completion(.failure(error)) return } if let quantity = results?.sumQuantity() { let exerciseTime = quantity.doubleValue(for: .minute()) completion(.success(exerciseTime)) } else { completion(.failure(NSError(domain: "HealthManager", code: -3, userInfo: [NSLocalizedDescriptionKey: "No exercise time data found for today."]))) } } healthStore.execute(query) } func fetchTodayStandHours(completion: @escaping(Result) -> Void) { guard HKHealthStore.isHealthDataAvailable() else { completion(.failure(NSError(domain: "HealthManager", code: -2, userInfo: [NSLocalizedDescriptionKey: "Health data unavailable"]))) return } let stand = HKCategoryType(.appleStandHour) let predicate = HKQuery.predicateForSamples(withStart: .startOfDay, end: Date()) let query = HKSampleQuery(sampleType: stand, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { _, results, error in if let error = error { completion(.failure(error)) return } guard let samples = results as? [HKCategorySample], !samples.isEmpty else { completion(.failure(NSError(domain: "HealthManager", code: -3, userInfo: [NSLocalizedDescriptionKey: "No stand data found for today."]))) return } // Count stand hours let standHours = samples.filter { $0.value == HKCategoryValueAppleStandHour.stood.rawValue }.count completion(.success(Double(standHours))) } healthStore.execute(query) } }