class HealthKitQueryBuilder
import Foundation
import HealthKit
class HealthKitQueryBuilder:ObservableObject {
let healthStore: HKHealthStore
let dateFormatter = DateFormatter()
@Published var hourlyStpCount: [HealthData]?
init(healthStore: HKHealthStore) {
self.healthStore = healthStore
}
func readHourlyStepCount(){
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss"
var hourlyStepCount = [HealthData]()
guard let stepCountType = HKObjectType.quantityType(forIdentifier: .stepCount) else {
fatalError("*** Unable to get the step count type ***")
}
var interval = DateComponents()
interval.hour = 1
let calendar = Calendar.current
let anchorDate = calendar.date(bySettingHour: 0, minute: 55, second: 0, of: Date())
let query = HKStatisticsCollectionQuery.init(quantityType: stepCountType,
quantitySamplePredicate: nil,
options: .cumulativeSum,
anchorDate: anchorDate!,
intervalComponents: interval)
query.initialResultsHandler = { query, results, error in
let startDate = calendar.date(byAdding: .hour,value: -24, to: Date())
DispatchQueue.main.async {
results?.enumerateStatistics(from: startDate!,to: Date(), with: { (result, stop) in
hourlyStepCount.append(HealthData(unit: "count", startDate: self.dateFormatter.string(from: result.startDate) , endDate: self.dateFormatter.string(from: result.endDate), value: (result.sumQuantity()?.doubleValue(for: HKUnit.count()) ?? 0)))
})
print("Hourly step count : \(hourlyStepCount)")
self.hourlyStpCount = hourlyStepCount
}
}
healthStore.execute(query)
}
}
class DataPointsJSONBuilder
import Foundation
import HealthKit
import SwiftUI
class DataPointsJSONBuilder {
let healthStore: HKHealthStore
@ObservedObject var queryBuilder: HealthKitQueryBuilder
init(healthStore: HKHealthStore) {
self.healthStore = healthStore
self.queryBuilder = HealthKitQueryBuilder(healthStore: healthStore)
}
func createJSON() ->String?{
queryBuilder.readHourlyStepCount()
let totalStepCount = queryBuilder.hourlyStpCount
let averageRestingHeartRate = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let averageHeartRateVariability = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let averageRespiratoryRate = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let totalSleepDuration = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let heartRate = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let systolicBloodPressure = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let diastolicBloodPressure = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let oxygenSaturation = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let currentGlucose = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let averageGlucose = [HealthData(unit: "count", startDate: "2022-11-22 10:55:00 PM +0000", endDate: "2022-11-22 11:55:00 PM +0000", value: 100.0)]
let dataPoints = DataPointsObj(totalStepCount: totalStepCount, averageRestingHeartRate: [], averaHeartRatevariability: [], averageRespiratoryRate: [], totalSleepDuration: [], heartRate: [], systolicBloodPressure: [], diastolicBloodPressure: [], oxygenSaturation: [], currentGlucoseValue: [], averageGlucoseValue: [])
guard let JSON = encodeToJSON(dataPointsObj: dataPoints) else {
return nil
}
return JSON
}
private func encodeToJSON(dataPointsObj: DataPointsObj) -> String? {
let encoder = JSONEncoder()
encoder.outputFormatting = .withoutEscapingSlashes
do {
let result = try encoder.encode(dataPointsObj)
if let jsonString = String(data: result, encoding: .utf8){
return jsonString
}
return nil
} catch {
return nil
}
}
}
I have above 2 classes implemented to read data from apple health kit and make a json object to send it to backend. But after the data is fetched data is not published to DataPointsJSONBuilder
class. I get data printed inside HealthKitQueryBuilder
class successfully and I have added the code for step count only for now. I call the createJSON
function inside onAppear
in the UI as follows.
.onAppear(){
print(DataPointsJSONBuilder(healthStore: healthStore).createJSON()!)
}
What I want to do is read data from healthkit and bring them to DataPointsJSONBuilder
class to send it to backend through a REST api. I don't understand why I hourlyStpCount is not updated when data is read. If there is a wrong implementation here, kindly correct me or suggest a method to solve my problem. Thanks!
I'd say that the problem here is that you are trying to use an asynchronous call as synchronous.
You use
The createJSON() function does have a return. Instead it should have an async approach. There are many ways to implement this - you tried to use one, the @Published functionality for example - but to make it simple, I am posting the way using callbacks (completion handlers).
You'll need to change a relatively large piece of code, but to make my point, at the end it should be something like:
In the DataPointsJSONBuilder:
And finally:
PS: Maybe you need to add the @escaping tag to the callbacks.