检查用户是否在 HealthKit 中授权了步骤

Check if user has authorised steps in HealthKit

我想知道如何在 HKStatisticsCollectoniQuery 中设置 completion 块。

整个想法是因为我想知道用户是否已接受获取实例步骤的权限。但根据 Apple 的文档,这是不可能的。

Apple 的 authorizationStatus(for:)

文档

This method checks the authorization status for saving data. To help prevent possible leaks of sensitive health information, your app cannot determine whether or not a user has granted permission to read data. If you are not given permission, it simply appears as if there is no data of the requested type in the HealthKit store. If your app is given share permission but not read permission, you see only the data that your app has written to the store. Data from other sources remains hidden.


所以我在考虑获取从 2014 年 1 月到今天的时间间隔之间的步骤。并查看该值是否仍为 0,这可能意味着两件事。要么是用户没有接受许可……要么是用户还没有走任何步骤。我不知道这是否是正确的方法。所以,如果有更好的方法,那么我将不胜感激!

好的,到目前为止我已经完成了以下功能:

func retrieveTotalCount(typeIdentifier: HKQuantityTypeIdentifier, completion: @escaping (_ total: Double) -> Void) {
    //   Define the Step Quantity Type
    let unitsCount = HKQuantityType.quantityType(forIdentifier: typeIdentifier)!


    // "1391269654" stands for january 1st 2014
    let initialDate = Date(timeIntervalSince1970: Double(1391269654))

    //  Set the Predicates & Interval
    let predicate = HKQuery.predicateForSamples(withStart: initialDate, end: Date(), options: .strictStartDate)
    var interval = DateComponents()
    interval.day = 1

    //  Perform the Query
    let query = HKStatisticsCollectionQuery(quantityType: unitsCount, quantitySamplePredicate: predicate, options: [.cumulativeSum], anchorDate: initialDate as Date, intervalComponents:interval)

    query.initialResultsHandler = { query, results, error in
        if error != nil {
            //  Something went Wrong
            return
        }

        if let myResults = results { 
            myResults.enumerateStatistics(from: initialDate, to: Date()) { statistics, stop in

                if let quantity = statistics.sumQuantity() {
                    if quantity.is(compatibleWith: HKUnit.meter()) {
                        let count = quantity.doubleValue(for: HKUnit.meter())
                        completion(count)
                    } else if quantity.is(compatibleWith: HKUnit.kilocalorie()) {
                        let count = quantity.doubleValue(for: HKUnit.kilocalorie())
                        completion(count)
                    } else {
                        let count = quantity.doubleValue(for: HKUnit.count())
                        completion(count)
                    }
                }
            }
        }
    }
    self.healthStore.execute(query)
}

每当我调用此函数时,它都会给我乘法完成,只是因为我正在检查每个统计信息。

override func viewDidLoad() {
    super.viewDidLoad()

    var counter = 0
    HealthKitManager.shared.retrieveTotalCount(typeIdentifier: .stepCount) { (count) in
        counter += 1
        print("Counter: \(counter)")
        // In my case the Counter will print out:
        // Counter: 1
        // Counter: 2
        // Counter: ...
        // Counter: n
    }
}

如何确保完成块只完成一次?

您有两个选择:使用跟踪异步操作的现有功能,或者使用可以为您执行此操作的框架。第二种选择,听说PromiseKit不错,但是没用过。

在类似的情况下,我设置了一个 DispatchQueue 并使用 DispatchGroup 来跟踪查询的完成情况:

let hkQueryGroup = DispatchGroup()
func runQuery (sampleType: HKQuantityType, start: Date, end: Date, name: String, quantityUnits: HKUnit) {
        hkQueryGroup.enter()
        let predicate = HKQuery.predicateForSamples(withStart: start, end: end, options: .strictStartDate)
        let query = HKStatisticsQuery(quantityType: sampleType,
                                      quantitySamplePredicate: predicate,
                                      options: .cumulativeSum)
        {_, result, error in
            if error != nil {
                // failure is still valid completion
                // do something with the data
                hkQueryGroup.leave()
                return
            }
            if let quantity = result?.sumQuantity()?.doubleValue(for: quantityUnits) {
                // do something with the data
            }
            else {
                //Returned *no* value for the quantity, which should not happen
            }
            hkQueryGroup.leave()
        }
        store.execute(query)
    }
    runQuery(sampleType: stepsType, start: startDate, end: endDate, name: "steps",quantityUnits: countUnit)
    runQuery(sampleType: distanceType, start: startDate, end: endDate, name: "distance",quantityUnits: distanceUnit)
    hkQueryGroup.notify(queue: DispatchQueue.main) {
        // do whatever you want when everything is finished)
        // to make sure all writes to the data dictionary have completed -- consider a barrier task in the queue.
    }

确定您的应用程序是否可以访问任何步骤的最有效方法是对限制为 1 且没有谓词或排序描述符的步骤执行单个 HKSampleQuery。您不需要在不同的时间段执行多个查询。