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

5t7ly7z5  于 2023-05-27  发布在  Swift
关注(0)|答案(2)|浏览(236)

我正试图获得过去7天的睡眠样本,并存储它们,并随时添加我想要的。我使用HKSampleQuery来获取所有的样本,并存储仅在床上的样本,但有时我会得到多个时间间隔重叠的样本。如何在没有任何重叠的情况下获得正确的睡眠数据?我使用下面的代码来获取数据

func readSleep(from startDate: Date?, to endDate: Date?,Id:Int, Completion: @escaping (Double,Date,Date,Int,String)->Void) {
        var sleepingHoursCount = 0.0
        let healthStore = HKHealthStore()
        
        guard let sleepType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis) else {
            return
        }
        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictEndDate)
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        
        let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { (query, result, error) in
            if error != nil {
                return
            }
            guard let result = result else{
                Completion(sleepingHoursCount,startDate!,endDate!,Id,"")
                return
            }
//            do{
                if let sleepSamples = result as? [HKCategorySample] {
                    for sample in sleepSamples {
                        guard let sleepValue = HKCategoryValueSleepAnalysis(rawValue: sample.value) else {
                            return
                        }
                        let isInBed = sleepValue == .inBed
                        if(isInBed){
                            let diffSeconds = sample.endDate.timeIntervalSinceReferenceDate - sample.startDate.timeIntervalSinceReferenceDate
                            sleepingHoursCount += diffSeconds
                            Completion(diffSeconds/3600,sample.startDate,sample.endDate,Id,source)
                        }
                    }
                }
        }
        healthStore.execute(query)
    }
xtupzzrd

xtupzzrd1#

这个Swift函数从HealthKit商店读取睡眠数据,HealthKit商店是苹果HealthKit框架的一部分,为iPhone和Apple Watch上的健康和健身数据提供了一个中央存储库。
函数readSleep(from:to:id:completion:)有四个参数:

  • startDate: Date?endDate: Date?是可选的日期,指示提取睡眠数据的开始和结束日期。
  • id: Int似乎是通过完成处理程序传递回调用者的某种标识符。
  • completion: @escaping (Double, Date, Date, Int, String) -> Void是一个completion handler闭包。当获取睡眠数据时调用该函数,闭包接收总睡眠时间(以秒为单位)(Double)、开始和结束日期(Date)、传递的标识符(Int)和一个字符串参数。

该函数从HealthKit存储读取睡眠数据,计算总睡眠持续时间(以秒为单位),并使用此数据调用完成处理程序。
但是,此功能中有一些方面可以改进:
1.错误处理未完成。当执行HKSampleQuery时发生错误时,它只是从函数返回。更合适的做法是同时调用完成处理程序并向调用方给予有关错误的信息。
1.该函数对completion闭包中的startDate和endDate使用强制展开。在Swift中,通常不鼓励强制解包,因为如果解包的值是nil,则可能导致运行时错误。最好安全地打开这些可选值。
1.函数似乎使用了一个未在函数范围内定义的变量源。下面是该函数的一个改进版本,它解决了这些问题:

func readSleep(from startDate: Date?, to endDate: Date?, id: Int, completion: @escaping (Double?, Date?, Date?, Int, String?) -> Void) {
    var sleepingHoursCount = 0.0
    let healthStore = HKHealthStore()
    
    guard let sleepType = HKObjectType.categoryType(forIdentifier: .sleepAnalysis) else {
        completion(nil, nil, nil, id, "HealthKit Sleep Analysis data type not available.")
        return
    }
    
    let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictEndDate)
    let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
    
    let query = HKSampleQuery(sampleType: sleepType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { (query, result, error) in
        if let error = error {
            completion(nil, nil, nil, id, "Query Error: \(error.localizedDescription)")
            return
        }
        
        guard let result = result else {
            completion(nil, startDate, endDate, id, "No sleep data found.")
            return
        }
        
        if let sleepSamples = result as? [HKCategorySample] {
            for sample in sleepSamples {
                guard let sleepValue = HKCategoryValueSleepAnalysis(rawValue: sample.value) else {
                    completion(nil, nil, nil, id, "Invalid sleep data found.")
                    return
                }
                
                if sleepValue == .inBed {
                    let diffSeconds = sample.endDate.timeIntervalSinceReferenceDate - sample.startDate.timeIntervalSinceReferenceDate
                    sleepingHoursCount += diffSeconds
                    completion(diffSeconds / 3600, sample.startDate, sample.endDate, id, nil)
                }
            }
            completion(sleepingHoursCount / 3600, startDate, endDate,
pbossiut

pbossiut2#

我尝试了这个解决方案https://stackoverflow.com/a/63600255/13882733,它采用重叠或不重叠时间间隔的不同样本,并跳过重叠的样本。我觉得挺好的。

相关问题