@wrlqwe
2016-12-11T15:24:16.000000Z
字数 5478
阅读 1431
iOS
HealthKit 设计用作 APP 间共享用户的健康数据,它约定了数据类型和单位,以便所有 APP 都能理解。
HealthKit 会将数据加密后保存在 iPhone 设备的 HealthStore 里,这些数据包括以下几类:
特征数据 Characteristic data
包括生日、性别、血型、肤色、轮椅使用等用户的特征数据,App 可以访问但是不能修改这些数据。
样本数据 Sample data
用户的大多数健康数据以
样本
的形式保存,样本代表了某一时间的数据。
来源数据 Source data
每个
样本
都保存了来源信息,其中HKSourceRevision
保存 APP 信息,HKDevice
保存硬件信息。
删除的数据 Deleted objects
删除的数据是指被用户或者其他 APP 删掉的数据。
HKObject 是 HealthKit 所有类的父类,包含这些属性:
- UUID 每个对象的唯一标示符。
- SourceRevision 数据的来源。来源可以是直接把数据存进HealthKit的设备,或者是应用。当一个对象保存进HealthKit中时,HealthKit会自动设置其来源。只有从HealthKit中获取的数据source属性才可用。
- Device 代表了创建数据的设备。
- Metadata 一个包含关于该对象额外信息的字典。元数据包含预定义和自定义的键。预定义的键用来帮助在应用间共享数据。自定义的键用来扩展HealthKit对象类型,为对象添加针对应用的数据。
所有样本都继承自 HKSample ,它定义了以下属性
- sampleType 代表着Sample的类型,比如睡眠,计步等。
- startDate 起始时间
- endDate 结束时间
每个具体样本都以子类方式使用,它们包括以下几类:
- HKCategorySample 分类样本,包括睡眠统计、站立统计等
- HKQuantitySample 数量样本,包括身高、体重、步数等,绝大多数样本都定义在这里
- HKDocumentSample 文档样本,目前只有 HKCDADocumentSample 一个实现
- HKCorrelation 复合数据,包含一个或多个样本。
- HKWorkout 锻炼活动,像跑步、游泳,甚至游戏。Workout 通常有类型、时长、距离、和消耗能量这些属性。你还可以为一个 workout 关联许多详细的样本。不像 correlation ,这些样本是不包含在 workout 里的。但是,它们可以通过 workout 获取到。
每个 HKObject 都对应一个 HKObjectType, HKObjectType 有跟 HKObject 相似的类层次设计,在查询和构造 HKObject 时都要指定 HKObjectType, 所有 HKObjectType 的子类实例都由 HKObjectType 的 class 方法创建。
HKObjectType 的实例都由 HKObjectType + 对应的 Identifier 来创建,
HKSample 与他们的 HKObjectType、HKTypeIdentifier、Sample 的单位,都有严格的对应关系
NSHealthShareUsageDescription
需要读取Health数据的权限来让应用支持XX功能
NSHealthUpdateUsageDescription
需要写入Health数据的权限来让应用支持XX功能
func askForPermit(_ callback: @escaping UIResponseCallback = { _ in }) {
var shareSet = Set<HKSampleType>()
shareSet.insert(HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!)
shareSet.insert(HKObjectType.quantityType(forIdentifier: .stepCount)!)
shareSet.insert(HKObjectType.workoutType())
var readSet: Set<HKObjectType> = shareSet
readSet.insert(HKObjectType.activitySummaryType())
readSet.insert(HKObjectType.characteristicType(forIdentifier: .biologicalSex)!)
readSet.insert(HKObjectType.categoryType(forIdentifier: .appleStandHour)!)
//检查是否所有权限都被允许
let allPermited = readSet.map { healthStore.authorizationStatus(for: $0) == .sharingAuthorized }.reduce(true) { lastResult, newValue in
return lastResult && newValue
}
if !allPermited {
//请求许可
healthStore.requestAuthorization(toShare: shareSet, read: readSet, completion: { (succeed, error) in
print("____\(succeed)\n\(error)")
})
}
}
首先构建相应的 Sample,一般 Sample 都是用各自的子类,由它所允许的 HKObjectType 和 对应的值构建,通过 HKHealthStore 的 save 方法写入。
下面就记录了一个持续十分钟的小睡:
func makeCategory(_ callback: @escaping UIResponseCallback = { _ in }) {
//构造一个起止时间
let dates = makeDateFrom(ago: 160, lasts: 10)
let sample = HKCategorySample(type: HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!, value: HKCategoryValueSleepAnalysis.asleep.rawValue,
start: dates.startDate, end: dates.endDate)
healthStore.save(sample) { complete, error in
let succeed = complete ? "成功" : "失败"
ui(callback, "\(#function)\n\(succeed)\n\(error)")
}
}
下面是一个游泳的 Workout,它记录了连续32分钟,包含两个运动片段和中间2分钟休息:
func makeSwimmingWorkoutTest(_ callback: @escaping UIResponseCallback = { _ in }) {
let intervalDates = makeDateFrom(ago: 66, lasts: 15)
let dates = makeDateFrom(ago: 66, lasts: 32)
let event0 = HKWorkoutEvent(type: .resume, date: intervalDates.startDate)
let event1 = HKWorkoutEvent(type: .pause, date: intervalDates.endDate)
let event2 = HKWorkoutEvent(type: .resume, date: Date(timeInterval: 60 * 2, since: intervalDates.endDate))
let event3 = HKWorkoutEvent(type: .pause, date: dates.endDate)
let workout = HKWorkout(activityType: .swimming, start: dates.startDate, end: dates.endDate, workoutEvents: [event0, event1, event2, event3], totalEnergyBurned: nil, totalDistance: HKQuantity(unit: HKUnit.meter(), doubleValue: 750), totalSwimmingStrokeCount: HKQuantity(unit: HKUnit.count(), doubleValue: 240), device: nil, metadata: nil)
healthStore.save(workout) { complete, error in
let succeed = complete ? "成功" : "失败"
ui(callback, "_____\(succeed)\n\(error)")
}
}
其他种类的 Sample 也类似。
HealthKit 提供了多种查询数据的方式,API里都以Query结尾,它们有的可以查询Sample, 有的可以查询概要信息,有的可以查询统计信息,等等。
下面是 HealthKit 提供的查询方式:
其中我认为比较重要的有 HKActivitySummaryQuery、HKSampleQuery 和 HKStatisticsQuery,它们分别对应三类查询请求,分别是概要信息、Sample 详情和统计信息。
概要信息按天统计,反映出用户每天的运动情况
Sample 详情则给开发者详细的信息,以便单独处理,merge 数据
统计信息则可以借由 HealthKit 的 merge 算法,直接返回处理后的数据
在开发中,统计信息应该是当下大多数应用统计步数的方式,下面是获取当天步数的方式:
func queryTodayStepCountByStatisticsQuery(_ callback: @escaping UIResponseCallback = { _ in }) {
let now = Date()
let today = now.startOfThisDay()
let predicate = HKQuery.predicateForSamples(withStart: today, end: now, options: [HKQueryOptions.strictStartDate])
let query = HKStatisticsQuery(quantityType: HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!, quantitySamplePredicate: predicate, options: [.cumulativeSum, .separateBySource]) { [unowned self] query, statistics, error in
guard let statistics = statistics else {
return
}
ui(callback, self.readStatistics(statistics))
}
healthStore.execute(query)
}
其中 statistics 包含了每个 Source 的统计结果,开发者既可以直接使用 statistics.sumQuantity()
获取全部步数,也可以使用 statistics.sumQuantity(for: source)
取用一部分源的数据。
目前qq运动步数使用的就是前者,而微信则处理灵活些,笔者尝试作弊步数,一开始微信运动会计数,几分钟后刷新步数,则在微信上又恢复如初,应该是过滤掉了这个 source 的结果,但是具体算法还不确定。