2

I have this function that has a completion handler that is to be returned after the task is finished, but from the output I am getting, it shows that the completion handler is being considered completed and returned before the task is done.

Function being called:

private func getSteps(completion: (Int) -> ()) {
    var val: Int = 0
    guard let sampleType = HKObjectType.quantityType(forIdentifier: .stepCount) else { return }
    let startDate = Calendar.current.startOfDay(for: Date())
    let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: .strictEndDate)
    var interval = DateComponents()
    
    interval.day = 1
    let query = HKStatisticsCollectionQuery(quantityType: sampleType, quantitySamplePredicate: predicate, options: [.cumulativeSum], anchorDate: startDate, intervalComponents: interval)
    query.initialResultsHandler = { query,result,error in
        if let myresult = result {
            myresult.enumerateStatistics(from: startDate, to: Date()) { (statistic, value) in
                if let count = statistic.sumQuantity() {
                    val = Int(count.doubleValue(for: HKUnit.count()))
                }
            }
        }
    }
    healthStore.execute(query)
    completion(val)
}

When I print the completion handler of the function, it prints 0 instead of Int(count.doubleValue(for: HKUnit.count())) which val was set to inside the function. Any input on why I am getting 0 instead of the set val would be greatly appreciated!

pawello2222
  • 46,897
  • 22
  • 145
  • 209

1 Answers1

3

Note that query.initialResultsHandler is asynchronous and your code will execute in the following order:

private func getSteps(completion: (Int) -> ()) {
    // #1
    var val: Int = 0
    ...
    // #2
    query.initialResultsHandler = { query,result,error in
        // #5
        if let myresult = result {
            myresult.enumerateStatistics(from: startDate, to: Date()) { (statistic, value) in
                if let count = statistic.sumQuantity() {
                    // #6
                    val = Int(count.doubleValue(for: HKUnit.count()))
                }
            }
        }
        // #7
    }
    // #3
    healthStore.execute(query)
    // #4
    completion(val)
}

Note that #4 is executed before #6.


The solution is to move completion inside the asynchronous block:

private func getSteps(completion: (Int) -> ()) {
    ...
    query.initialResultsHandler = { query,result,error in
        if let myresult = result {
            myresult.enumerateStatistics(from: startDate, to: Date()) { (statistic, value) in
                if let count = statistic.sumQuantity() {
                    val = Int(count.doubleValue(for: HKUnit.count()))
                }
            }
        }
        completion(val) // <- move here
    }
    healthStore.execute(query)
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209