iOS开发之HealthKit的使用

1,293 阅读3分钟

我正在参加「掘金·启航计划」

本文主要介绍iOS中获取健康中的一些数据的方法

1. HealthKit使用

我们新建项目,之后提供HealthKit功能 image.png

我们在info.plist中添加权限

image.png

  • Privacy - Health Share Usage Description
    允许 app 获取健康中的数据

  • Privacy - Health Update Usage Description
    允许 app 更新健康中的数据

  • Privacy - Health Records Usage Description
    允许 app 记录数据到健康中

我们在使用的地方进行授权

let isSupport = HKHealthStore .isHealthDataAvailable()

如果支持的话我们定义下我们想要获取和写入的类型,其中有的后面才有的,比如统计洗手的次数就是在iOS14之后

            /// 心率

            let heartRate =  HKQuantityType .quantityType(forIdentifier: .heartRate)

            /// 体重

            let bodyMass =  HKQuantityType .quantityType(forIdentifier: .bodyMass)

            /// 高度

            let height =  HKQuantityType .quantityType(forIdentifier: .height)

            /// 步数

            let stepCount =  HKQuantityType .quantityType(forIdentifier: .stepCount)

            /// 睡眠

            let sleep =  HKObjectType.categoryType(forIdentifier: .sleepAnalysis)

            /// 卡路里活动能量

            let energy = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)

            /// 温度

            let bodyTemperature = HKQuantityType.quantityType(forIdentifier: .bodyTemperature)

            /// 血糖

            let bloodGlucose = HKQuantityType.quantityType(forIdentifier: .bloodGlucose)

            let set = NSSet(objects: height!,bodyMass!,heartRate!,stepCount!,bodyTemperature!,bloodGlucose!)

            var redSet = NSSet(objects: height!,bodyMass!,heartRate!,stepCount!,sleep!,energy!,bodyTemperature!,bloodGlucose!)

            if #available(iOS 14.0, *) {

                /// 洗手

                let handwashing = HKQuantityType.categoryType(forIdentifier: .handwashingEvent)

                redSet = NSSet(objects: height!,bodyMass!,heartRate!,stepCount!,sleep!,energy!,handwashing!,bloodGlucose!,bodyTemperature!)

            }

我们发送权限请求

let healthStore = HKHealthStore.init()

            

            healthStore.requestAuthorization(toShare: set as? Set<HKSampleType>, read: redSet as? Set<HKObjectType>) { sucess, error in

                

                print(sucess)

            }

image.png

2. HealthKit数据读取

我们读取类型HKTypeIdentifier,主要有2种。

  • HKQuantityTypeIdentifier

image.png

有些我们使用的,我们结合实际情况进行选择

  • HKCategoryTypeIdentifier

我在有的HKQuantityTypeIdentifier没有找到我想要的类型,比如睡眠

image.png

CategoryType在未来将会取代,但是目前有的类型还是要通过CategoryType获取

image.png

我们定义2个方法来获取不同类型的数据

@objc protocol GetHealthInfoProtocol {

    

    /// 根据类型获取健康信息,比如身高,体重等

    @objc optional func getHealthInfoOfQuantityType(type:HKQuantityTypeIdentifier, _ unit:HKUnit?, completion: (@escaping (Double) -> Void));

    /// 根据类型获取健康信息,比如身高,体重等

    @objc optional  func getHealthInfoOfCategoryType(type:HKCategoryTypeIdentifier, _ unit:HKUnit?, completion: (@escaping ([HKSample]) -> Void));

    

}

具体实现

let healthStore = HKHealthStore()

    func getHealthInfoOfCategoryType(type: HKCategoryTypeIdentifier, _ unit:HKUnit?, completion: @escaping (([HKSample]) -> Void)) {

        let categoryType = HKQuantityType.categoryType(forIdentifier: type)!

        let calendar = Calendar.current

        let now = NSDate()

        let componsssss :Set<Calendar.Component> = [Calendar.Component.day,Calendar.Component.month,Calendar.Component.year]

        var components = calendar.dateComponents(componsssss, from: now as Date)

        components.hour = 0

        components.minute = 0

        components.second = 0

        

        let startDate = calendar.date(from: components)

        let endDate = calendar.date(byAdding: Calendar.Component.day, value: 1, to: startDate!)

        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)

        let query = HKSampleQuery.init(sampleType: categoryType,

                                       predicate: predicate,

                                       limit: HKObjectQueryNoLimit,

                                       sortDescriptors:  [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)]){ _, results, _ in

            guard let result = results else {

                return completion([])

            }

            

            return completion(result)

        }

        healthStore.execute(query)

    }

这里我们使用HKSampleQuery进行查询,这个通常是返回一个数组。这里我定义的是一天的时间

  • HKStatisticsQuery
let query = HKStatisticsQuery(quantityType: quantityType, quantitySamplePredicate: predicate, options: .cumulativeSum) { _, result, _ in

                

                guard let result = result, let sum = result.sumQuantity() else {

                    return completion(0.0)

                }

                let doubleValue = sum.doubleValue(for: unit ?? HKUnit.count())

                return completion(doubleValue)

            }

            

            healthStore.execute(query)

返回的是HKStatistics类型:表示一段时间内数量样本的统计信息

3. HealthKit数据写入

也是根据我们type类型定义2种不同的方法

  • HKQuantityTypeIdentifier

通过初始化HKQuantitySample对象,写入我们的数据

/// 保存信息

    func saveUserHealthInfoOfCategoryType(type:HKCategoryTypeIdentifier,startDate:Date,endDate:Date,_ unit:HKUnit?,num:Int,completion: (@escaping (Bool) -> Void)) {

        let categoryType = HKCategoryType.categoryType(forIdentifier: type)!

        let categorySample =  HKCategorySample.init(type: categoryType, value: num, start: startDate, end: endDate)

   
        healthStore.save(categorySample) { sucess, error in

            completion(sucess)

            if sucess {

                print("写入数据成功")


            }else {

                print("写入数据成功")

            }

        }      

    }
  • HKCategoryTypeIdentifier

初始化HKCategorySample类型,写入数据

func saveUserHealthInfoOfCategoryType(type:HKCategoryTypeIdentifier,startDate:Date,endDate:Date,_ unit:HKUnit?,num:Int,completion: (@escaping (Bool) -> Void)) {

        let categoryType = HKCategoryType.categoryType(forIdentifier: type)!

        let categorySample =  HKCategorySample.init(type: categoryType, value: num, start: startDate, end: endDate)

        

        healthStore.save(categorySample) { sucess, error in

            completion(sucess)

            if sucess {

                print("写入数据成功")

  


            }else {

                print("写入数据成功")

  


            }

        }
    }

4. 注意

我们这里会有一个时间的选择,获取当天时间

let calendar = Calendar.current

        let now = NSDate()

        let componsssss :Set<Calendar.Component> = [Calendar.Component.day,Calendar.Component.month,Calendar.Component.year]

        var components = calendar.dateComponents(componsssss, from: now as Date)

        components.hour = 0

        components.minute = 0

        components.second = 0

        

        var startDate = calendar.date(from: components)

        var endDate = calendar.date(byAdding: Calendar.Component.day, value: 1, to: startDate!)

获取现在时间到之前一天的

Calendar.current.date(byAdding: DateComponents(day: -1), to: Date())!

具体自己调整

let startDate = Calendar.current.date(byAdding: DateComponents(hour: -1,minute: -1,second: -1), to: Date())!

另外我们的HKUnit类型也是要按规矩的写入,不然崩溃,具体参考,比如心率

HKUnit.init(from: "count/min")

image.png

demo展示

iShot_2022-10-14_09.34.13.gif

代码地址HealthKitDemo