Swift Apple HealthKit 读写苹果健康数据

2,150 阅读3分钟

单例模式

这里使用单例来管理苹果健康,新建一个HealthKitManager,内部需要实例化一个HKHealthStore,进行读写等操作均需要这个对象。

private lazy var healthStore = HKHealthStore()

并且扩展HKObjectType所需要的数据类别,在此列举一些常用的:

extension HKObjectType {
    // 身高
    static let heightType = HKQuantityType.quantityType(forIdentifier: .height)!
    // 体重
    static let weightType = HKQuantityType.quantityType(forIdentifier: .bodyMass)!
    // 步数
    static let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
    // 步行+跑步距离
    static let distanceType = HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning)!
    // 活动能量
    static let activeEnergyType = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)!
    // 心率
    static let heartRateType = HKQuantityType.quantityType(forIdentifier: .heartRate)!
    // 体能训练
    static let workoutType = HKObjectType.workoutType()
    // 体能训练路线
    static let workoutRouteType = HKObjectType.seriesType(forIdentifier: HKWorkoutRouteTypeIdentifier)!
}

以及对应HKUnit需要用的数据单位,按实际需求自定义数据单位:

extension HKUnit {
    static let heightUnit = HKUnit.meterUnit(with: .centi)
    static let weightUnit = HKUnit.gramUnit(with: .kilo)
    static let stepUnit = HKUnit.count()
    static let timeUnit = HKUnit.second()
    static let distanceUnit = HKUnit.meter()
    static let calorieUnit = HKUnit.kilocalorie()
    static let heartRateUnit = HKUnit(from: "count/min")
}

授权健康权限

苹果是非常注重用户的隐私权限,因此想要读写获取【健康】中的数据的第一步是要先授权权限。

定义需要读和写的数据类别

    /// 获取[写]权限,配置需要向用户的健康中写入的数据
    static func dataTypesToWrite() -> Set<HKSampleType> {
        let set: Set<HKSampleType> = Set([.heightType, .weightType, .stepType, .distanceType, .activeEnergyType, .workoutType])
        return set
    }
    
    /// 获取[读]权限,可以读取用户健康中的数据
    static func dataTypesToRead() -> Set<HKObjectType> {
        let set: Set<HKObjectType> = Set([.stepType, .distanceType, .workoutType, .workoutRouteType, .heartRateType])
        return set
    }

主要调用open func requestAuthorization(toShare typesToShare: Set<HKSampleType>?, read typesToRead: Set<HKObjectType>?, completion: @escaping (Bool, Error?) -> Void)方法来进行读写权限的授权,详见我另一篇文章苹果权限授权统一管理使用介绍。

1、步数写入健康

func writeStep(with step: Double, start startDate: Date, end endDate: Date) {
        // 定义一个步数单位数据对象
        let quantity = HKQuantity(unit: .stepUnit, doubleValue: step)
        // 定义一个数据对象样本
        let sample = HKQuantitySample(type: .stepType, quantity: quantity, start: startDate, end: endDate)
        healthStore.save(sample) { success, error in
            if success {
                PPP("步数设置成功")
            } else {
                PPP(error?.localizedDescription)
            }
        }
    }

2、体能训练写入健康

func writeWorkout(distance: Double, duration: Double, calorie: Double, start startDate: Date, end endDate: Date) {
        // 定义一个距离单位数据对象
        let distanceQuantity = HKQuantity(unit: .distanceUnit, doubleValue: distance)
        // 定义一个卡路里单位数据对象
        let calorieQuantity = HKQuantity(unit: .calorieUnit, doubleValue: calorie)
        // 定义一个体能训练对象
        let sample = HKWorkout(activityType: .running, start: startDate, end: endDate, duration: duration, totalEnergyBurned: calorieQuantity, totalDistance: distanceQuantity, metadata: nil)
        healthStore.save(sample) { success, error in
            if success {
                PPP("体能训练设置成功")
            } else {
                PPP(error?.localizedDescription)
            }
        }
    }

1、从健康读取步数

func readStep(from: Date, to: Date, handler: @escaping (_ step: Int) -> ()) {
        let predicate = HKQuery.predicateForSamples(withStart: from, end: to, options: [])
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
        let query = HKSampleQuery(sampleType: .stepType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { _, samples, error in
            guard error == nil, let results = samples as? [HKQuantitySample] else {
                handler(0)
                return
            }
            // 过滤手表产生的步数
            let watchSteps = results.filter { $0.sourceRevision.productType?.contains("Watch") == true }
            // map自定义单位步数值
            let steps = watchSteps.map { Int($0.quantity.doubleValue(for: .stepUnit)) }
            // 计算总步数
            let step = steps.reduce(0) { $0 + $1 }
            handler(step)
        }
        healthStore.execute(query)
    }

2、从健康读取体能训练

func readWorkout(from: Date, to: Date, handler: @escaping ([HKWorkout]) -> ()) {
        let predicate = HKQuery.predicateForSamples(withStart: from, end: to, options: [])
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
        let query = HKSampleQuery(sampleType: .workoutType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor]) { _, samples, error in
            guard error == nil, let workouts = samples as? [HKWorkout] else {
                handler([])
                return
            }
            // 过滤手表产生且根据项目需要只想要的运动类别数据(爬山、徒步、跑步、步行、骑行、游泳等)的体能训练
            let watchWorkouts = workouts.filter { $0.sourceRevision.productType?.contains("Watch") == true && (HKT.HKWorkoutActivityRunTypes.contains($0.workoutActivityType) || HKT.HKWorkoutActivityOtherTypes.contains($0.workoutActivityType)) }
            handler(watchWorkouts)
        }
        healthStore.execute(query)
    }

Apple HealthKit健康数据的使用大体就以上粗略内容,可以根据项目需要,自行获取解析更多数据。

联系方式:kim77895pl@gmail.com

作者Kim,Hope it helps for you。2022.9.27