класс 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)
}
}
класс 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
}
}
}
У меня есть выше 2 классов, реализованных для чтения данных из набора здоровья Apple и создания объекта json для отправки его на серверную часть. Но после извлечения данных данные не публикуются в классе DataPointsJSONBuilder. Я успешно печатаю данные внутри класса HealthKitQueryBuilder, и я добавил код для подсчета шагов только на данный момент. Я вызываю функцию createJSON внутри onAppear в пользовательском интерфейсе следующим образом.
.onAppear(){
print(DataPointsJSONBuilder(healthStore: healthStore).createJSON()!)
}
Что я хочу сделать, так это прочитать данные из HealthKit и перенести их в класс DataPointsJSONBuilder, чтобы отправить их на серверную часть через REST API. Я не понимаю, почему я hourlyStpCount не обновляется при чтении данных. Если здесь есть неправильная реализация, поправьте меня или предложите способ решения моей проблемы. Спасибо!





Я бы сказал, что проблема здесь в том, что вы пытаетесь использовать асинхронный вызов как синхронный.
Ты используешь
print(DataPointsJSONBuilder(healthStore: healthStore).createJSON()!)
Функция createJSON() имеет возврат. Вместо этого он должен иметь асинхронный подход. Есть много способов реализовать это — вы пытались использовать один из них, например, функциональность @Published — но для простоты я публикую способ, используя обратные вызовы (обработчики завершения).
Вам нужно будет изменить относительно большой кусок кода, но, чтобы подчеркнуть мою точку зрения, в конце он должен быть примерно таким:
func readHourlyStepCount(callback: ([HealthData]) -> ()) {
//implementation
callback(hourlyStepCount)
//...
}
В DataPointsJSONBuilder:
func createJSON(callback: (String?) -> ()) {
//implementation
callback(JSON)
//...
}
И наконец:
DataPointsJSONBuilder(healthStore: healthStore).createJSON { json in
print(json)
}
PS: Возможно, вам нужно добавить тег @escaping в обратные вызовы.
да, определенно, должен был сделать двойную проверку. Уже отредактировано. Спасибо за ответ.
Это решение работает. Но скажем, есть несколько методов, таких как readHourlyStepCount. В этом случае мне нужно взять результат всех этих методов и создать JSON. Есть идеи, как это сделать?
Вы имеете в виду, что у вас есть более одного асинхронного вызова, и вам нужно «что-то сделать», когда они все закончатся? Если это то, что вы говорите, вам следует поискать «как обрабатывать несколько асинхронных вызовов» в Google. Если у вас все еще есть проблемы после исследования, вы можете опубликовать еще один вопрос с новым заголовком, описанием,... PS: Есть много подходов к этому, лично я бы использовал функцию архивирования Combine.
Да, это второй вопрос, который у меня есть, но, как вы сказали, я должен задать его в отдельном вопросе. Во всяком случае, я использовал DispatchGroup, чтобы решить эту проблему.
Вы не можете использовать
@ObservableObjectвне представления SwiftUI. Также ваша функцияcreateJSONиспользуетhourlyStpCountсразу после вызоваreadHourlyStepCount, но запрос HealthKit будет выполняться асинхронно через некоторое время. Вы можете использовать подписчика Combine для получения подсчета шагов, как только он станет доступен.