Swift: публикация данных из одного класса в другой не работает (Apple HealthKit)

класс 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 не обновляется при чтении данных. Если здесь есть неправильная реализация, поправьте меня или предложите способ решения моей проблемы. Спасибо!

Вы не можете использовать @ObservableObject вне представления SwiftUI. Также ваша функция createJSON использует hourlyStpCount сразу после вызова readHourlyStepCount, но запрос HealthKit будет выполняться асинхронно через некоторое время. Вы можете использовать подписчика Combine для получения подсчета шагов, как только он станет доступен.

Paulw11 19.12.2022 10:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я бы сказал, что проблема здесь в том, что вы пытаетесь использовать асинхронный вызов как синхронный.

Ты используешь

 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 в обратные вызовы.

да, определенно, должен был сделать двойную проверку. Уже отредактировано. Спасибо за ответ.

Jorge Poveda 19.12.2022 11:27

Это решение работает. Но скажем, есть несколько методов, таких как readHourlyStepCount. В этом случае мне нужно взять результат всех этих методов и создать JSON. Есть идеи, как это сделать?

Krishan 19.12.2022 14:36

Вы имеете в виду, что у вас есть более одного асинхронного вызова, и вам нужно «что-то сделать», когда они все закончатся? Если это то, что вы говорите, вам следует поискать «как обрабатывать несколько асинхронных вызовов» в Google. Если у вас все еще есть проблемы после исследования, вы можете опубликовать еще один вопрос с новым заголовком, описанием,... PS: Есть много подходов к этому, лично я бы использовал функцию архивирования Combine.

Jorge Poveda 20.12.2022 10:00

Да, это второй вопрос, который у меня есть, но, как вы сказали, я должен задать его в отдельном вопросе. Во всяком случае, я использовал DispatchGroup, чтобы решить эту проблему.

Krishan 21.12.2022 16:08

Другие вопросы по теме