单例模式
这里使用单例来管理苹果健康,新建一个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