diff --git a/BeeKit/HeathKit/CategoryHealthKitMetric.swift b/BeeKit/HeathKit/CategoryHealthKitMetric.swift index 4d5aa462..2d555e1b 100644 --- a/BeeKit/HeathKit/CategoryHealthKitMetric.swift +++ b/BeeKit/HeathKit/CategoryHealthKitMetric.swift @@ -16,6 +16,8 @@ public class CategoryHealthKitMetric : HealthKitMetric { public let category : HealthKitCategory let hkSampleType : HKSampleType + public var precision: [HKUnit : Int] { [:] } + internal init(humanText: String, databaseString: String, category : HealthKitCategory, hkSampleType: HKSampleType) { self.humanText = humanText self.databaseString = databaseString diff --git a/BeeKit/HeathKit/HealthKitConfig.swift b/BeeKit/HeathKit/HealthKitConfig.swift index 73ad41fa..d4cce37b 100644 --- a/BeeKit/HeathKit/HealthKitConfig.swift +++ b/BeeKit/HeathKit/HealthKitConfig.swift @@ -15,7 +15,7 @@ public enum HealthKitConfig { // Activity QuantityHealthKitMetric(humanText: "Active energy", databaseString: "activeEnergy", category: .Activity, hkQuantityTypeIdentifier: .activeEnergyBurned, precision: [HKUnit.largeCalorie(): 0]), QuantityHealthKitMetric(humanText: "Cycling distance", databaseString: "cyclingDistance", category: .Activity, hkQuantityTypeIdentifier: .distanceCycling), - QuantityHealthKitMetric(humanText: "Exercise time", databaseString: "exerciseTime", category: .Activity, hkQuantityTypeIdentifier: .appleExerciseTime), + QuantityHealthKitMetric(humanText: "Exercise time", databaseString: "exerciseTime", category: .Activity, hkQuantityTypeIdentifier: .appleExerciseTime, precision: [HKUnit.minute(): 1]), QuantityHealthKitMetric(humanText: "Nike Fuel", databaseString: "nikeFuel", category: .Activity, hkQuantityTypeIdentifier: .nikeFuel), QuantityHealthKitMetric(humanText: "Resting energy", databaseString: "basalEnergy", category: .Activity, hkQuantityTypeIdentifier: .basalEnergyBurned, precision: [HKUnit.largeCalorie(): 0]), StandHoursHealthKitMetric(humanText: "Stand hours", databaseString: "standHour", category: .Activity), @@ -49,7 +49,7 @@ public enum HealthKitConfig { QuantityHealthKitMetric(humanText: "Vitamin D", databaseString: "dietaryVitaminD", category: .Nutrition, hkQuantityTypeIdentifier: .dietaryVitaminD), QuantityHealthKitMetric(humanText: "Vitamin E", databaseString: "dietaryVitaminE", category: .Nutrition, hkQuantityTypeIdentifier: .dietaryVitaminE), QuantityHealthKitMetric(humanText: "Vitamin K", databaseString: "dietaryVitaminK", category: .Nutrition, hkQuantityTypeIdentifier: .dietaryVitaminK), - QuantityHealthKitMetric(humanText: "Water", databaseString: "water", category: .Nutrition, hkQuantityTypeIdentifier: .dietaryWater), + QuantityHealthKitMetric(humanText: "Water", databaseString: "water", category: .Nutrition, hkQuantityTypeIdentifier: .dietaryWater, precision: [HKUnit.fluidOunceUS(): 1]), // Sleep TimeInBedHealthKitMetric(humanText: "Time in bed", databaseString: "timeInBed", category: .Sleep), diff --git a/BeeKit/HeathKit/HealthKitMetric.swift b/BeeKit/HeathKit/HealthKitMetric.swift index d952ac75..b0ae5db3 100644 --- a/BeeKit/HeathKit/HealthKitMetric.swift +++ b/BeeKit/HeathKit/HealthKitMetric.swift @@ -20,6 +20,7 @@ public protocol HealthKitMetric { var humanText : String { get } var databaseString : String { get } var category : HealthKitCategory { get } + var precision : [HKUnit: Int] { get } /// The permission required for this connection to read data from HealthKit func permissionType() -> HKObjectType @@ -41,3 +42,13 @@ public protocol HealthKitMetric { /// The units this metric returns its datapoint values in func units(healthStore : HKHealthStore) async throws -> HKUnit } + +extension HealthKitMetric { + func applyPrecision(value: Double, unit: HKUnit) -> Double { + if let unitPrecision = precision[unit] { + let roundingFactor = pow(10.0, Double(unitPrecision)) + return round(value * roundingFactor) / roundingFactor + } + return value + } +} diff --git a/BeeKit/HeathKit/QuantityHealthKitMetric.swift b/BeeKit/HeathKit/QuantityHealthKitMetric.swift index 6656a382..3ee8d07c 100644 --- a/BeeKit/HeathKit/QuantityHealthKitMetric.swift +++ b/BeeKit/HeathKit/QuantityHealthKitMetric.swift @@ -116,10 +116,7 @@ class QuantityHealthKitMetric : HealthKitMetric { continue } - if let unitPrecision = precision[unit] { - let roundingFactor = pow(10.0, Double(unitPrecision)) - datapointValue = round(datapointValue * roundingFactor) / roundingFactor - } + datapointValue = applyPrecision(value: datapointValue, unit: unit) let id = "apple-health-" + daystamp.description results.append(NewDataPoint(requestid: id, daystamp: daystamp, value: NSNumber(value: datapointValue), comment: "Auto-entered via Apple Health")) diff --git a/BeeKit/HeathKit/WorkoutMinutesHealthKitMetric.swift b/BeeKit/HeathKit/WorkoutMinutesHealthKitMetric.swift index ecc4bc7d..653f1578 100644 --- a/BeeKit/HeathKit/WorkoutMinutesHealthKitMetric.swift +++ b/BeeKit/HeathKit/WorkoutMinutesHealthKitMetric.swift @@ -5,6 +5,8 @@ import OSLog public class WorkoutMinutesHealthKitMetric : CategoryHealthKitMetric { private let logger = Logger(subsystem: "com.beeminder.beeminder", category: "WorkoutMinutesHealthKitMetric") let minuteInSeconds = 60.0 + + public override var precision: [HKUnit: Int] { return [HKUnit.minute(): 1] } init(humanText: String, databaseString: String, category: HealthKitCategory) { @@ -16,7 +18,7 @@ public class WorkoutMinutesHealthKitMetric : CategoryHealthKitMetric { let samplesOnDay = samples.filter{sample in sample.startDate >= startOfDate} let workouts = samplesOnDay.compactMap({sample in sample as? HKWorkout}) let workoutMinutes = workouts.map{sample in sample.duration / minuteInSeconds}.reduce(0, +) - return Double(workoutMinutes) + return applyPrecision(value: Double(workoutMinutes), unit: HKUnit.minute()) } public override func recentDataPoints(days: Int, deadline: Int, healthStore: HKHealthStore, autodataConfig: [String: Any]) async throws -> [BeeDataPoint] { @@ -40,7 +42,7 @@ public class WorkoutMinutesHealthKitMetric : CategoryHealthKitMetric { let workouts = samples.compactMap { $0 as? HKWorkout } for workout in workouts { - let workoutMinutes = workout.duration / minuteInSeconds + let workoutMinutes = applyPrecision(value: workout.duration / minuteInSeconds, unit: HKUnit.minute()) let workoutDescription = formatWorkoutDescription(workout: workout) let id = "apple-health-workout-\(workout.uuid.uuidString)"