123 lines
4.4 KiB
Swift
123 lines
4.4 KiB
Swift
//
|
|
// 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<Double, Error>) -> 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<Double, Error>) -> 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<Double, Error>) -> 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)
|
|
}
|
|
}
|
|
|