You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Apple Health获取AirPods音频时长代码返回0的问题排查

问题分析与修复方案

一、异步执行时序错误(核心原因)

HKSampleQuery是异步执行的,你在调用healthStore.execute(query)后立刻打印totalDuration,此时查询还未启动,闭包里的遍历计算代码完全没执行,自然看不到"+"的打印,totalDuration也保持初始值0.0。所有依赖查询结果的逻辑(计算时长、打印结果)必须放在查询的回调闭包内部,而非外部。

二、未应用AirPods过滤条件

你已经定义了airPodsPredicate用于筛选Apple设备的音频数据,但初始化HKSampleQuery时传入的是predicate: nil,等于放弃了过滤逻辑,这会导致查询所有设备的音频数据(如果存在的话),但不是结果为0的直接原因。

三、变量作用域与线程问题

外部的totalDuration被闭包捕获,但查询回调运行在后台队列,即便后续修改了totalDuration,外部的打印操作也早已执行完毕。如果后续需要更新UI,必须切换回主线程(打印操作虽无强制要求,但建议养成规范)。

四、潜在的权限配置问题

确保项目已正确配置HealthKit相关权限:

  • 在Info.plist中添加NSHealthReadUsageDescription字段,填写读取健康数据的原因说明
  • 在Xcode项目配置的"Signing & Capabilities"中开启HealthKit能力

修复后的完整代码

func main() {
    // 检查设备是否支持HealthKit
    guard HKHealthStore.isHealthDataAvailable() else {
        print("HealthKit is not available on this device.")
        return
    }
    print("HealthKit is available on this device.")

    // 初始化HKHealthStore实例
    let healthStore = HKHealthStore()

    // 获取音频暴露事件类型
    guard let audioCategory = HKObjectType.categoryType(forIdentifier: .audioExposureEvent) else {
        print("Unable to create the audio exposure category type.")
        return
    }

    // 请求用户授权读取音频数据
    healthStore.requestAuthorization(toShare: nil, read: [audioCategory]) { (success, error) in
        guard success else {
            if let error = error {
                print("Failed to request authorization: \(error.localizedDescription)")
            }
            return
        }
        
        // 构建AirPods设备筛选条件
        let airPodsPredicate = HKQuery.predicateForObjects(withMetadataKey: HKMetadataKeyDeviceManufacturerName, operatorType: .equalTo, value: "Apple")
        
        // 创建查询实例
        let query = HKSampleQuery(sampleType: audioCategory, predicate: airPodsPredicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { (query, samples, error) in
            guard let audioSamples = samples as? [HKCategorySample] else {
                if let error = error {
                    print("Failed to retrieve audio samples: \(error.localizedDescription)")
                }
                return
            }
            
            var totalDuration: TimeInterval = 0
            
            // 计算AirPods音频播放总时长
            for sample in audioSamples {
                totalDuration += sample.endDate.timeIntervalSince(sample.startDate)
                print("+")
            }
            
            // 若需更新UI,必须切换至主线程
            DispatchQueue.main.async {
                print("Total duration of audio played by AirPods: \(totalDuration) seconds")
            }
        }
        
        // 执行查询
        healthStore.execute(query)
    }
}

额外说明

  • 闭包内部定义totalDuration更合理,避免外部变量的捕获冲突问题
  • 若仍查询不到数据,确认iPhone的健康APP中是否存在AirPods的音频暴露记录
  • .audioExposureEvent记录的是音频暴露事件,并非所有音频播放时长,若需获取完整播放时长,需确认是否有对应HealthKit类型支持

内容的提问来源于stack exchange,提问作者Jake

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 10:12:25