【问题标题】:SwiftUI - DispatchQueue.main.async in HealthKitSwiftUI - HealthKit 中的 DispatchQueue.main.async
【发布时间】:2021-05-24 09:46:37
【问题描述】:

我想在 SwiftUI 中加载以下 GUI:

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var test = Test()
    @ObservedObject var healthStore = HealthStore()
    
    
    func callUpdate() {
        
        print(test.value)
        print(healthStore.systolicValue)
        print(healthStore.diastolicValue)
    }
    
    var body: some View {
        Text("Platzhalter")
            .padding()
            .onAppear(perform: {
            healthStore.setUpHealthStore()
                callUpdate()
            })
        
        Button("Test"){
            callUpdate()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

变量 healthStore.systolicValue 和 healthStore.diastolicValue 是通过函数 callUpdate() 调用的。在第一次调用时,两个变量都为零。只有当我通过Test按钮调用该函数时,才会在控制台中输出正确的值。

变量 healthStore.systolicValue 和 healthStore.diastolicValue 在 HealthStore 类中计算:

import Foundation
import HealthKit

class HealthStore: ObservableObject {

    var healthStore: HKHealthStore?
    var query: HKStatisticsQuery?
    
    public var systolicValue: HKQuantity?
    public var diastolicValue: HKQuantity?

    init() {
        if HKHealthStore.isHealthDataAvailable() {
            healthStore = HKHealthStore()
        }
    }

    func setUpHealthStore() {
        let typesToRead: Set = [
            HKQuantityType.quantityType(forIdentifier: .bloodPressureSystolic)!,
            HKQuantityType.quantityType(forIdentifier: .bloodPressureDiastolic)!
        ]

        healthStore?.requestAuthorization(toShare: nil, read: typesToRead, completion: { success, error in
            if success {
                print("requestAuthrization")
                self.calculateBloodPressureSystolic()
                self.calculateBloodPressureDiastolic()
            }
        })

    }

    func calculateBloodPressureSystolic() {
        guard let bloodPressureSystolic = HKObjectType.quantityType(forIdentifier: .bloodPressureSystolic) else {
            // This should never fail when using a defined constant.
            fatalError("*** Unable to get the bloodPressure count ***")
        }
        query = HKStatisticsQuery(quantityType: bloodPressureSystolic,
                                  quantitySamplePredicate: nil,
                                  options: .discreteAverage) {
            query, statistics, error in

            DispatchQueue.main.async{
                self.systolicValue = statistics?.averageQuantity()
            }
        }

        healthStore!.execute(query!)
    }
    
    func calculateBloodPressureDiastolic() {
        guard let bloodPressureDiastolic = HKObjectType.quantityType(forIdentifier: .bloodPressureDiastolic) else {
            // This should never fail when using a defined constant.
            fatalError("*** Unable to get the bloodPressure count ***")
        }
        query = HKStatisticsQuery(quantityType: bloodPressureDiastolic,
                                  quantitySamplePredicate: nil,
                                  options: .discreteAverage) {
            query, statistics, error in

            DispatchQueue.main.async{
                self.diastolicValue = statistics?.averageQuantity()
            }
        }

        healthStore!.execute(query!)
    }
    
    
}

当我调用 ContentView 时,如何修改我的代码以直接获取 healthStore.systolicValue 和 healthStore.diastolicValue 的正确值?

【问题讨论】:

    标签: swiftui healthkit


    【解决方案1】:

    这是我用于测试并为我工作的完整代码, 使用 macos 11.4,xcode 12.5,目标 ios 14.5,在 iPhone 设备上测试。 如果这对您不起作用,请告诉我们。

    import SwiftUI
    import HealthKit
    
    @main
    struct TestErrorApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
    
    class HealthStore: ObservableObject {
        var healthStore: HKHealthStore?
        var query: HKStatisticsQuery?
        @Published var systolicValue: HKQuantity?
        @Published var diastolicValue: HKQuantity?
        
        init() {
            if HKHealthStore.isHealthDataAvailable() {
                healthStore = HKHealthStore()
            }
        }
    
        func setUpHealthStore() {
            let typesToRead: Set = [
                HKQuantityType.quantityType(forIdentifier: .bloodPressureSystolic)!,
                HKQuantityType.quantityType(forIdentifier: .bloodPressureDiastolic)!
            ]
            healthStore?.requestAuthorization(toShare: nil, read: typesToRead, completion: { success, error in
                if success {
                    print("--> requestAuthorization")
                    self.calculateBloodPressureSystolic()
                    self.calculateBloodPressureDiastolic()
                }
            })
        }
        
        func calculateBloodPressureSystolic() {
            guard let bloodPressureSystolic = HKObjectType.quantityType(forIdentifier: .bloodPressureSystolic) else {
                // This should never fail when using a defined constant.
                fatalError("*** Unable to get the bloodPressure count ***")
            }
            query = HKStatisticsQuery(quantityType: bloodPressureSystolic,
                                      quantitySamplePredicate: nil,
                                      options: .discreteAverage) {
                query, statistics, error in
                DispatchQueue.main.async{
                  //  self.systolicValue = statistics?.averageQuantity()
                    self.systolicValue = HKQuantity(unit: HKUnit(from: ""), doubleValue: 1.2)
                    print("----> calculateBloodPressureSystolic statistics: \(statistics)")
                    print("----> calculateBloodPressureSystolic error: \(error)")
                    print("----> calculateBloodPressureSystolic: \(self.systolicValue)")
                }
            }
            healthStore!.execute(query!)
        }
        
        func calculateBloodPressureDiastolic() {
            guard let bloodPressureDiastolic = HKObjectType.quantityType(forIdentifier: .bloodPressureDiastolic) else {
                // This should never fail when using a defined constant.
                fatalError("*** Unable to get the bloodPressure count ***")
            }
            query = HKStatisticsQuery(quantityType: bloodPressureDiastolic,
                                      quantitySamplePredicate: nil,
                                      options: .discreteAverage) {
                query, statistics, error in
                DispatchQueue.main.async{
                   // self.diastolicValue = statistics?.averageQuantity()
                    self.diastolicValue = HKQuantity(unit: HKUnit(from: ""), doubleValue: 3.4)
                    print("----> calculateBloodPressureDiastolic statistics: \(statistics)")
                    print("----> calculateBloodPressureDiastolic error: \(error)")
                    print("----> calculateBloodPressureDiastolic: \(self.diastolicValue)")
                }
            }
            healthStore!.execute(query!)
        }
    }
    
    struct ContentView: View {
        @ObservedObject var healthStore = HealthStore()
        var bloodPressureStandard = HKQuantity(unit: HKUnit(from: ""), doubleValue: 0.0)
        
        var body: some View {
            VStack {
                Text("systolicValue: \(healthStore.systolicValue ?? bloodPressureStandard)")
                Text("diastolicValue: \(healthStore.diastolicValue ?? bloodPressureStandard)")
            }.onAppear {
                healthStore.setUpHealthStore()
            }
        }
    }
    

    这是我得到的输出:

    --> 请求授权

    ----> 计算血压收缩统计:无

    ----> calculateBloodPressureSystolic 错误:可选(错误域=com.apple.healthkit 代码=11“指定谓词没有可用数据。” UserInfo={NSLocalizedDescription=指定谓词没有可用数据。})

    ----> 计算血压收缩压:可选(1.2())

    ----> 计算血压舒张压统计:无

    ----> calculateBloodPressureDiastolic 错误:可选(错误域=com.apple.healthkit Code=11“指定谓词没有可用数据。” UserInfo={NSLocalizedDescription=指定谓词没有可用数据。})

    ---->calculateBloodPressureDiastolic:可选(3.4())

    UI 显示:

    收缩值:1.2 ()

    舒张压值:3.4 ()

    【讨论】:

      【解决方案2】:

      我想你快到了。您的 HealthStore 需要为 收缩值和舒张值。不需要“callUpdate()”函数。

      我会修改你的代码如下:

      (注意一旦计算出 systolicValue 和 diastolicValue,模型会更新,视图也会自动更新)

      struct ContentView: View {
          @ObservedObject var healthStore = HealthStore()
          
          var body: some View {
              VStack {
                  Text("Platzhalter")
                  Text("systolicValue: \(healthStore.systolicValue)")
                  Text("diastolicValue: \(healthStore.diastolicValue)")
              }.onAppear {
                  healthStore.setUpHealthStore()
              }
          }
      }
      
      class HealthStore: ObservableObject {
      
          var healthStore: HKHealthStore?
          var query: HKStatisticsQuery?
          
          @Published var systolicValue: HKQuantity?    //  <----
          @Published var diastolicValue: HKQuantity?   //  <----
      
          ...
          
      }
      

      【讨论】:

        【解决方案3】:

        @workindog 我按照建议更改了我的代码,但我仍然没有从 healthStore 获得计算值。

        @ObservedObject var healthStore = HealthStore()
        var bloodPressureStandard = HKQuantity(unit: HKUnit(from: ""), doubleValue: 0.0)
        
        
        var body: some View {
                VStack {
                    
                    Text("systolicValue: \(healthStore.systolicValue ?? bloodPressureStandard)")
                    Text("diastolicValue: \(healthStore.diastolicValue ?? bloodPressureStandard)")
                }.onAppear {
                    healthStore.setUpHealthStore()
                    
                }
            }
        

        【讨论】:

          【解决方案4】:

          您确定数据可用吗?要检查,你能把它放在 HealthStore 中吗:

              DispatchQueue.main.async{
                //  self.systolicValue = statistics?.averageQuantity()
                  self.systolicValue = HKQuantity(unit: HKUnit(from: ""), doubleValue: 1.2)
                  print("----> calculateBloodPressureSystolic statistics: \(statistics)")
                  print("----> calculateBloodPressureSystolic error: \(error)")
                  print("----> calculateBloodPressureSystolic: \(self.systolicValue)")
              }
          

          diastolicValue 也是如此。

          【讨论】:

          • 这是我的输出:----> calculateBloodPressureSystolic 统计:可选( HKQuantityTypeIdentifierBloodPressureSystolic 的统计(2021-05-05 19:17:00 +0000 - 2021-05- 05 21:59:00 +0000) 过源 ((null))>) ----> calculateBloodPressureSystolic 错误:nil ----> calculateBloodPressureSystolic:可选(1.2())
          【解决方案5】:

          我发现了我的错误:

          我在 HealthStore 类中声明了变量 systolicValue 和 diastolicValue 错误。我将它们声明为公共而不是@Published。正确的代码是:

          @Published var systolicValue: HKQuantity?
          @Published var diastolicValue: HKQuantity?
          

          感谢您的帮助。

          【讨论】:

          • 很高兴听到您找到了原因。请把答案标记为正确,谢谢。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-01
          • 1970-01-01
          • 2020-10-17
          • 2023-03-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多