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

基于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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:06:33