最近在做項目的時候,突然接到產品經理給的一個需求,要求在項目中統計用戶每日步數,具體要求就是獲取手機健康應用中統計的每日步數,然后以圖形方式顯示。
剛聽到這個需求的時候就想到了蘋果在iOS8系統推出之時順帶出的HealthKit框架,不過對于其API倒是非常的陌生。于是就開始在網上查找資料,百度,谷歌一番之后,發現也未能找到一個資料能夠很好地引導新手使用該框架(可能是我沒找到好的-_-!),經過幾天的個人摸索,也算是有點心得體會,在這里就將自己對該框架相關的使用做個梳理,也希望能夠為接下來即將接觸和使用該框架的童鞋提供一點小小的幫助。如有什么疑問或者不對的地方歡迎提出。以下內容以獲取步數為例,其他數據獲取可以此類推。
一、項目中關聯HealthKit框架
首先填寫好你項目的Bundle Identifier并且選好Team(這兩個東西最好事先設置好,以免之后又得重新關聯),然后在項目物理文件結構中點選對應的項目,在TARGETS中選擇你自身的項目,再在右側選擇Capabilities選項
從中找到HealthKit這項,點擊右側的開關開啟,當出現中間紅框所示的內容,表示項目與HealthKit框架關聯成功了
你會在項目中看到多了HealthKit.framework和.entitlements結尾的這兩個文件,OK一切順利,接下來就可以Code了。
二、HealthKit所支持的系統和設備
因為HealthKit框架是在iOS8系統出來之時一同推出的,所以該框架目前只支持iOS8及以上系統,目前支持的設備有iPhone、iWatch,要記得iPad是不支持的哦,如果你的代碼同時支持iPhone和iPad設備,那么記得判斷下設備還有系統版本號,以免出現不必要的奔潰現象。在項目中導入<HealthKit/HealthKit.h>
后,你也可以使用以下代碼判斷該設備的系統能否使用健康數據:
[HKHealthStore isHealthDataAvailable]
三、應用授權
要想獲取健康數據中的步數,則需要通過用戶許可才行。具體可以使用以下代碼進行授權:
HKHealthStore *healthStore = [[HKHealthStore alloc] init];
NSSet *readObjectTypes = [NSSet setWithObjects:[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount], nil];
[healthStore requestAuthorizationToShareTypes:nil readTypes:readObjectTypes completion:^(BOOL success, NSError *error) {
//進行一些操作
}];
這里調用了requestAuthorizationToShareTypes: readTypes: completion:
方法,用于對應用授權需要獲取和分享的健康數據:
1、第一個參數傳入一個
NSSet
類型數據,用于告知用戶,我的app可能會在你的健康數據庫中修改這些選項數據(顯然目前我們不需要,傳nil)
2、第二個參數也是傳入NSSet
類型數據,告知用戶,我的app可能會從你的數據庫中讀取以下幾項數據
3、第三個是授權許可回調,這里的BOOL值success
不能用于區分用戶是否允許應用向數據庫存取數據,這一點評論區有人提到,我也對此進行了測試,發現確實即使用戶不允許,該值也為YES
四、獲取健康步數
授權完成之后,我們接下來就可以調用API來獲取數據庫數據了。
HKSampleType *sampleType = [HKSampleType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:nil endDate:nil options:HKQueryOptionStrictStartDate];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:HKSampleSortIdentifierStartDate ascending:YES];
HKSampleQuery *sampleQuery = [[HKSampleQuery alloc] initWithSampleType:sampleType predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:@[sortDescriptor] resultsHandler:^(HKSampleQuery *query, NSArray *results, NSError *error) {
if(!error && results) {
for(HKQuantitySample *samples in results) {
NSLog(@"%@ 至 %@ : %@", samples.startDate, samples.endDate, samples.quantity);
}
} else {
//error
}
}];
[healthStore executeQuery:sampleQuery];
這段代碼主要做了以下幾件事情:
1、第一段通過傳入一個枚舉值HKQuantityTypeIdentifierStepCount來創建一個樣品類的實例,用于告知,我接下來要獲取的數據是步數>2、第二段代碼通過創建一個NSPredicate類的實例,用于獲取在某個時間段的數據,這里startDate和endDate傳入nil,表示獲取全部數據,第三個參數傳入一個Option,里面有三個值,這個參數我試驗了下不同的值代入,發現返回的結果都是一樣的,要是有誰知道這個值是做什么用的麻煩告知我一聲~
3、第三段代碼創建了一個NSSortDescriptor類實例,用于對查詢的結果排序
4、第四段代碼通過調用HKSampleQuery類的實例方法獲取所需數據
5、最后一行代碼用于執行數據查詢操作
通過這段代碼獲取的數據,打印出來會發現,它獲取的是比較詳盡的數據,精確到每一小段時間從開始時間到結束時間內所獲取的步數。
五、數據采集
有時候需求并不需要了解這么詳盡的數據,只希望獲取每小時、每天或者每月的步數,那么我們就需要用到另一個新類HKStatisticsCollectionQuery
進行數據的分段采集
HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
dateComponents.day = 1;
HKStatisticsCollectionQuery *collectionQuery = [[HKStatisticsCollectionQuery alloc] initWithQuantityType:quantityType quantitySamplePredicate:nil options: HKStatisticsOptionCumulativeSum | HKStatisticsOptionSeparateBySource anchorDate:[NSDate dateWithTimeIntervalSince1970:0] intervalComponents:dateComponents];
collectionQuery.initialResultsHandler = ^(HKStatisticsCollectionQuery *query, HKStatisticsCollection * __nullable result, NSError * __nullable error) {
for (HKStatistics *statistic in result.statistics) {
NSLog(@"\n%@ 至 %@", statistic.startDate, statistic.endDate);
for (HKSource *source in statistic.sources) {
if ([source.name isEqualToString:[UIDevice currentDevice].name]) {
NSLog(@"%@ -- %f",source, [[statistic sumQuantityForSource:source] doubleValueForUnit:[HKUnit countUnit]]);
}
}
}
};
[healthStore executeQuery:collectionQuery];
1、第一段代碼所做的和之前的一樣,定義需要獲取的數據為步數
2、第二段代碼創建一個NSDateComponents類實例,設置我要獲取的步數時間間隔,這里設置為按天統計,這里也可以設置按小時或者按月統計
3、第三段代碼創建查詢統計對象collectionQuery
,通過傳入四個參數進行初始化:
1、第一個參數同上面一樣,設置需要查詢的類型
2、第二個參數傳入一個NSPredicate實例,目前這里傳nil
3、第三個參數是關鍵,傳入一個Option可選值,告訴查詢統計對象我需要獲取的是啥,這里傳入HKStatisticsOptionCumulativeSum | HKStatisticsOptionSeparateBySource
值,獲取時間段的步數和以及將數據根據不同的數據來源進行分段
4、第四個參數傳入一個錨點,類似于數組的索引值,查詢將會從改錨點開始查詢,這里可以根據不同的錨點值,獲取日/周/月/年數據
4、第四段代碼是將collectionQuery
對象的block屬性initialResultsHandler
進行賦值,該block會在數據查詢成功之后進行回調,從中可以獲得我們想要的數據
5、最后一行執行該查詢統計操作執行這段代碼,通過打印的日志可以看到當前設備中存儲的按日間隔存儲的步行數總和了。
返回的數據中HKSource
對象中的name
可用于區分健康數據來源,一般只獲取設備中的步數,過濾其他第三方數據來源,目前微信、QQ所用的記步就是區分了不同的數據來源,防止作弊!??!
最后附上一個仿健康應用步數的Demo:
gitHub地址:https://github.com/MarsCWD/HealthKitDemo
參考鏈接:
http://vit0.com/blog/2014/10/30/ios-8-healthkit-jie-shao/
https://segmentfault.com/a/1190000003779081