Swift/HealthKit应用:如何用Task替代已弃用的Semaphore?
问题原因及解决方案
你的Task实现返回0的核心问题是:HK查询是异步执行的,但你的Task在调用healthStore.execute(query)后立刻返回了初始值0,此时查询的闭包还没来得及执行,goal根本没被赋值。
要将基于闭包的HealthKit查询适配到async/await模式,正确的做法是用withCheckedContinuation(或withCheckedThrowingContinuation处理错误)把回调式API包装成异步函数。以下是修正后的代码:
func getMoveGoal() async throws -> Int { let calendar = Calendar.current var startDateComponents = calendar.dateComponents([.year, .month, .day], from: Date()) startDateComponents.calendar = calendar let predicate = HKQuery.predicateForActivitySummary(with: startDateComponents) return try await withCheckedThrowingContinuation { continuation in let query = HKActivitySummaryQuery(predicate: predicate) { _, summariesOrNil, errorOrNil in // 处理错误 if let error = errorOrNil { continuation.resume(throwing: error) return } // 提取目标值 guard let summaries = summariesOrNil, let firstSummary = summaries.first else { continuation.resume(returning: 0) // 无数据时返回0,或根据需求抛错 return } let goal = Int(firstSummary.activeEnergyBurnedGoal.doubleValue(for: .kilocalorie())) continuation.resume(returning: goal) } healthStore.execute(query) } }
关键说明:
withCheckedContinuation会暂停当前任务,直到我们调用continuation.resume(),这样就能等待HK查询的闭包执行完成后再返回结果。- 必须确保
continuation.resume()只被调用一次,否则会引发运行时崩溃,所以要在错误、无数据、成功赋值等所有分支都处理到。 - 改用
throws可以把HealthKit的查询错误抛出去,让调用方处理,比直接返回0更严谨。
调用示例:
在SwiftUI视图或其他异步上下文里调用时,用await:
Task { do { let moveGoal = try await getMoveGoal() print("获取到的Move目标:\(moveGoal)") // 在这里使用目标值进行后续计算 } catch { print("查询失败:\(error)") } }
内容的提问来源于stack exchange,提问作者Joe Baldwin
相关产品推荐
相关产品推荐

