在Swift中从Apple Watch获取实时心率(按钮触发,非后台运行)
Apple Watch 实时心率数据获取(按钮触发式)
以下是实现按钮触发式Apple Watch实时心率采集的分步方案:
1. 配置健康权限
首先需要在iOS和watchOS的Info.plist中添加健康数据访问权限描述:
- 添加
NSHealthReadUsageDescription键,值为应用读取心率数据的用途说明(例如:"获取实时心率数据用于健康状态监测") - 若你的应用还涉及写入健康数据,需额外添加
NSHealthShareUsageDescription键(本场景仅读取可忽略)
2. 引入HealthKit框架
在watchOS应用的目标中,确保已添加HealthKit框架(Xcode中可通过Build Phases -> Link Binary With Libraries添加)。
3. 核心代码实现
初始化HealthStore与权限检查
import HealthKit import WatchKit class HeartRateController: WKInterfaceController { private let healthStore = HKHealthStore() private var activeHeartRateQuery: HKQuery? @IBOutlet weak var heartRateDisplayLabel: WKInterfaceLabel! override func viewDidLoad() { super.viewDidLoad() checkHealthKitAvailabilityAndPermissions() } private func checkHealthKitAvailabilityAndPermissions() { guard HKHealthStore.isHealthDataAvailable() else { heartRateDisplayLabel.setText("设备不支持HealthKit") return } guard let heartRateType = HKObjectType.quantityType(forIdentifier: .heartRate) else { heartRateDisplayLabel.setText("心率数据类型不可用") return } let readPermissions: Set<HKObjectType> = [heartRateType] healthStore.requestAuthorization(toShare: nil, read: readPermissions) { [weak self] success, error in DispatchQueue.main.async { if let error = error { self?.heartRateDisplayLabel.setText("权限请求失败: \(error.localizedDescription)") } else if !success { self?.heartRateDisplayLabel.setText("未授权访问心率数据") } } } } }
按钮触发的心率采集与停止
// 开始监测按钮点击事件 @IBAction func startMonitoringTapped() { guard let heartRateType = HKObjectType.quantityType(forIdentifier: .heartRate) else { heartRateDisplayLabel.setText("心率类型不可用") return } // 停止已有查询,避免重复监听 if let query = activeHeartRateQuery { healthStore.stop(query) } // 创建锚定查询,获取最新心率并监听实时更新 let anchorQuery = HKAnchoredObjectQuery( type: heartRateType, predicate: nil, anchor: nil, limit: HKObjectQueryNoLimit ) { [weak self] _, samples, _, _, error in self?.handleHeartRateSamples(samples) } // 设置更新回调,接收实时新增的心率数据 anchorQuery.updateHandler = { [weak self] _, samples, _, _, error in self?.handleHeartRateSamples(samples) } activeHeartRateQuery = anchorQuery healthStore.execute(anchorQuery) heartRateDisplayLabel.setText("正在监测心率...") } // 停止监测按钮点击事件 @IBAction func stopMonitoringTapped() { if let query = activeHeartRateQuery { healthStore.stop(query) activeHeartRateQuery = nil heartRateDisplayLabel.setText("已停止监测") } } // 处理心率数据并更新UI private func handleHeartRateSamples(_ samples: [HKSample]?) { guard let heartRateSamples = samples as? [HKQuantitySample], let latestSample = heartRateSamples.last else { return } DispatchQueue.main.async { [weak self] in // 转换心率单位为次/分钟(BPM) let bpmUnit = HKUnit.count().unitDivided(by: HKUnit.minute()) let heartRate = latestSample.quantity.doubleValue(for: bpmUnit) self?.heartRateDisplayLabel.setText("当前心率: \(Int(heartRate)) BPM") } }
4. 关键注意事项
- UI更新必须在主线程:HealthKit的查询回调运行在后台线程,更新Watch UI时务必切换到主线程。
- 避免重复查询:每次启动新的监测前,停止之前的active查询,防止资源浪费和重复数据回调。
- 权限状态检查:用户可能在设置中随时撤销权限,建议在每次触发监测前再次检查权限状态。
- 无后台需求适配:本方案仅在用户点击按钮后启动监测,停止后即终止查询,无需配置任何后台模式,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Thomas Dini
相关产品推荐
相关产品推荐

