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

如何在Swift中从HealthKit获取无重叠的睡眠样本?

解决睡眠样本重叠问题的方法

要获取无重叠的正确睡眠数据,你需要对筛选后的inBed样本进行排序并合并重叠/相邻的时间区间,具体实现如下:

核心思路

  1. 先筛选出所有inBed类型的睡眠样本
  2. 按开始时间升序排序样本,确保从最早的睡眠时段开始处理
  3. 遍历排序后的样本,合并重叠或相邻的时间区间,避免重复计算重叠部分

修改后的完整代码

func readSleep(from startDate: Date?, to endDate: Date?, id: Int, completion: @escaping (Double, Date, Date, Int, String) -> Void) {
    let healthStore = HKHealthStore()
    
    guard let sleepType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis),
          let startDate = startDate,
          let endDate = endDate else {
        completion(0.0, startDate ?? Date(), endDate ?? Date(), id, "")
        return
    }
    
    let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictEndDate)
    // 改为按开始时间升序排序,为合并区间做准备
    let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
    
    let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { (query, result, error) in
        if let error = error {
            print("获取睡眠数据失败: \(error.localizedDescription)")
            completion(0.0, startDate, endDate, id, "")
            return
        }
        
        guard let sleepSamples = result as? [HKCategorySample] else {
            completion(0.0, startDate, endDate, id, "")
            return
        }
        
        // 筛选出inBed类型的样本
        let inBedSamples = sleepSamples.filter { sample in
            guard let sleepValue = HKCategoryValueSleepAnalysis(rawValue: sample.value) else {
                return false
            }
            return sleepValue == .inBed
        }
        
        guard !inBedSamples.isEmpty else {
            completion(0.0, startDate, endDate, id, "")
            return
        }
        
        // 合并重叠/相邻的时间区间
        var mergedIntervals: [(start: Date, end: Date, source: String)] = []
        var currentStart = inBedSamples[0].startDate
        var currentEnd = inBedSamples[0].endDate
        var currentSource = inBedSamples[0].sourceRevision.source.name
        
        for sample in inBedSamples.dropFirst() {
            if sample.startDate <= currentEnd {
                // 区间重叠或相邻,合并为一个区间
                currentEnd = max(currentEnd, sample.endDate)
                // 若需要保留所有来源,可改为拼接字符串,示例中取第一个来源
                // currentSource = "\(currentSource), \(sample.sourceRevision.source.name)"
            } else {
                // 新的独立区间,存入结果列表
                mergedIntervals.append((currentStart, currentEnd, currentSource))
                currentStart = sample.startDate
                currentEnd = sample.endDate
                currentSource = sample.sourceRevision.source.name
            }
        }
        // 存入最后一个合并后的区间
        mergedIntervals.append((currentStart, currentEnd, currentSource))
        
        // 计算总睡眠时长并返回每个合并后的区间数据
        var totalSleepHours = 0.0
        for interval in mergedIntervals {
            let durationSeconds = interval.end.timeIntervalSince(interval.start)
            let durationHours = durationSeconds / 3600
            totalSleepHours += durationHours
            completion(durationHours, interval.start, interval.end, id, interval.source)
        }
    }
    
    healthStore.execute(query)
}

关键细节说明

  • 排序调整:将原代码按结束时间降序改为按开始时间升序,确保合并区间的顺序正确
  • 合并逻辑:通过比较当前样本与已合并区间的时间关系,自动合并重叠部分,避免重复计算
  • 来源处理:示例中保留了睡眠数据的来源名称,可根据需求调整为拼接多个来源或优先保留特定来源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 10:12:43