基于Apple Healthkit的睡眠分析:如何计算前一晚各睡眠阶段时长
计算前一晚各睡眠阶段累计时长的实现方法
你当前的查询缺少时间范围过滤条件,会拉取全量历史睡眠样本,无法只统计前一晚的数据。整体实现分3步完成:
- 第一步:定义前一晚的统计时间窗口
苹果官方推荐用「统计日前一天18:00 到 统计日当天12:00」作为夜间睡眠的统计窗口,适配跨0点的睡眠场景,不会漏记凌晨的睡眠数据。 - 第二步:给查询追加时间谓词,只拉取目标窗口内的睡眠样本
- 第三步:按睡眠阶段分组,累加每个阶段下所有样本的时长
完整可运行代码示例
import HealthKit // 1. 构造前一晚的时间窗口 let calendar = Calendar.current let now = Date() // 取当天0点 let startOfToday = calendar.startOfDay(for: now) // 窗口起点:前一天18点 guard let windowStart = calendar.date(byAdding: .hour, value: -6, to: startOfToday), // 窗口终点:当天12点 let windowEnd = calendar.date(byAdding: .hour, value: 12, to: startOfToday) else { fatalError("睡眠统计时间窗口构造失败") } // 2. 构造组合查询条件 // 原有睡眠阶段过滤条件 let stagePredicate = HKCategoryValueSleepAnalysis.predicateForSamples(equalTo: .allAsleepValues) // 新增时间范围过滤条件,拉取所有和目标窗口有交集的样本 let timePredicate = HKQuery.predicateForSamples(withStart: windowStart, end: windowEnd, options: []) // 合并谓词,需同时满足阶段、时间两个条件 let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [stagePredicate, timePredicate]) let queryPredicate = HKSamplePredicate.sample(type: HKCategoryType(.sleepAnalysis), predicate: compoundPredicate) // 按睡眠开始时间正序排序,方便后续做重叠校验 let sleepQuery = HKSampleQueryDescriptor( predicates: [queryPredicate], sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)] ) // 3. 执行查询并统计各阶段时长 let sleepSamples = try await sleepQuery.result(for: healthStore) // 定义时长统计字典,key为睡眠阶段枚举,value为累计时长(单位:秒) var stageDurations: [HKCategoryValueSleepAnalysis: TimeInterval] = [ .asleepCore: 0, .asleepDeep: 0, .asleepREM: 0, .asleepUnspecified: 0 ] for sample in sleepSamples { // 转换为睡眠阶段枚举,过滤无效样本 guard let stage = HKCategoryValueSleepAnalysis(rawValue: sample.value) else { continue } // 计算当前样本时长 let sampleDuration = sample.endDate.timeIntervalSince(sample.startDate) // 累加到对应睡眠阶段 stageDurations[stage, default: 0] += sampleDuration } // 读取各阶段统计结果 let remTotal = stageDurations[.asleepREM] ?? 0 // REM睡眠总时长 let deepTotal = stageDurations[.asleepDeep] ?? 0 // 深度睡眠总时长 let coreTotal = stageDurations[.asleepCore] ?? 0 // 浅度/核心睡眠总时长 let unspecifiedTotal = stageDurations[.asleepUnspecified] ?? 0 // 未标记阶段的睡眠总时长 let totalSleep = remTotal + deepTotal + coreTotal + unspecifiedTotal // 整体睡眠总时长
注意事项
- 枚举值对应关系:
HKCategoryValueSleepAnalysis.asleepCore对应浅度睡眠(苹果官方命名为核心睡眠)、.asleepDeep对应深度睡眠、.asleepREM对应快速眼动睡眠,.asleepUnspecified为未标记具体阶段的睡眠,一般来自旧系统版本或第三方App写入的数据。 - 如果你需要统计睡眠中的清醒时长,可将
.asleepAwake加入统计字典,注意.allAsleepValues默认不包含卧床(.inBed)和睡眠中清醒的样本,需要单独调整谓词规则。 - 第三方睡眠App写入的数据可能存在时间区间重叠的问题,如需精确统计可在遍历时增加时间区间去重逻辑,系统原生睡眠监测数据默认无重叠,入门阶段可跳过该处理。
- 如果需要极高精度的统计,对于和时间窗口部分重叠的样本,可裁剪掉窗口外的时长部分再累加,系统原生睡眠数据一般不会出现超出常规窗口的情况。
内容的提问来源于stack exchange,提问作者kabir.987
相关产品推荐
相关产品推荐

