目前公司业务有用到获取Apple-Health里的步数和心率,所以自己初步了解了一下有关于iOS的HealthKit的简单实用
1.target->signing&Capabilities里面点击+capability选择HealthKit如下图所示
备注:可以不用勾选Capabilities (1.clinical Healtg Records;2.Background Delivery)
2.info.plist加上隐私权限
NSHealthShareUsageDescription
NSHealthUpdateUsageDescription
3.这个时候会自动生成一个EntitlementsFile
4.这个时候就可以着手代码部分。
新建一个HealthManager类
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
//当前健康数据授权的状态
typedef NS_ENUM(NSInteger ,SMHealthAuthorizationStatus){
SMHealthAuthorizationStatusNotDetermined = 0,
SMHealthAuthorizationStatusSharingDenied,
SMHealthAuthorizationStatusSharingAuthorized,
SMHealthAuthorizationStatusUnsupport,
};
@interface SMHealthManager : NSObject
//@property (nonatomic ,assign)HKAuthorizationStatus stauts;
+ (instancetype)defaultManager;
/**
请求用户授权
@param compltion 回调
*/
- (void)requestHealthKitAuthorization:(void(^)(BOOL success, NSError *error))compltion;
//获取当天步数
- (void)getStepCount:(void (^)(double stepCount, NSError *error))queryResultBlock;
/**
获取用户心率
@param completion 回调 出错时 value = @"--"
*/
- (void)fetchUserHeartRate:(void(^)(NSString *value, NSError *error))completion;
/**
检查设备是否支持健康数据
@return yes 支持
*/
- (BOOL)isHealthDataAvailable;
//返回当前权限状态
-(SMHealthAuthorizationStatus)appleHealthAuthorizationStatus;
@end
.m文件
#import "SMHealthManager.h"
#import <HealthKit/HealthKit.h>
#import <CoreMotion/CoreMotion.h>
#define IOS8 ([UIDevice currentDevice].systemVersion.floatValue >= 8.0f)
@interface SMHealthManager()
@property (nonatomic,strong) HKHealthStore *healthStore;
@property (nonatomic, strong) HKSample *currentHeartLastSample;
@property (nonatomic, strong) NSMutableArray *currentHeartRateSample;
@property (nonatomic, assign) double currentHeartRateBPM;
@property (nonatomic, strong) NSMutableArray *healthSteps;
/// 协处理器类
@property (nonatomic, strong) CMPedometer *pedometer;
@end
@implementation SMHealthManager
static SMHealthManager *instance = nil;
+ (instancetype)defaultManager
{
if (!instance) {
instance = [[super allocWithZone:NULL]init];
}
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
return [SMHealthManager defaultManager];
}
#pragma mark -授权状态
- (SMHealthAuthorizationStatus)appleHealthAuthorizationStatus
{
if (![self isHealthDataAvailable]) {
return SMHealthAuthorizationStatusSharingDenied;
}
SMHealthAuthorizationStatus healthStatus = SMHealthAuthorizationStatusSharingDenied;
HKObjectType *type = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKAuthorizationStatus status = [self.healthStore authorizationStatusForType:type];
switch (status) {
case HKAuthorizationStatusNotDetermined:
healthStatus = SMHealthAuthorizationStatusNotDetermined;
PCLog(@"苹果健康未授权");
break;
case HKAuthorizationStatusSharingDenied:
healthStatus = SMHealthAuthorizationStatusSharingDenied;
PCLog(@"苹果健康已拒绝");
break;
case HKAuthorizationStatusSharingAuthorized:
healthStatus = SMHealthAuthorizationStatusSharingAuthorized;
PCLog(@"苹果健康已授权");
break;
default:
break;
}
return healthStatus;
}
#pragma mark -授权结果
- (void)requestHealthKitAuthorization:(void (^)(BOOL, NSError * _Nonnull))compltion
{
if (![self isHealthDataAvailable]) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"iOS 系统低于8.0不能获取健康数据,请升级系统" forKey:NSLocalizedDescriptionKey];
NSError *aError = [NSError errorWithDomain:@"com.sleepmonitor.aio" code:0 userInfo:userInfo];
compltion(NO,aError);
return;
}
HKQuantityType *stepCountType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKQuantityType *heartRateType = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
//1.5.0先不上心率
NSSet *readTypes = [NSSet setWithObjects:stepCountType,nil];
[self.healthStore requestAuthorizationToShareTypes:readTypes readTypes:readTypes completion:^(BOOL success, NSError * _Nullable error) {
if (compltion) {
compltion(success,error);
}
}];
}
#pragma mark - GetStepCount
- (void)getStepCount:(void (^)(double stepCount, NSError *error))queryResultBlock {
HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKStatisticsQuery *query = [[HKStatisticsQuery alloc]initWithQuantityType:quantityType quantitySamplePredicate:[self predicateForSamplesToday] options:HKStatisticsOptionCumulativeSum completionHandler:^(HKStatisticsQuery * _Nonnull query, HKStatistics * _Nullable result, NSError * _Nullable error) {
if (error) {
// [self getCMPStepCount: queryResultBlock];
//获取昨天数据
__block double yesterdayStepCount = 0;
[self getYesterdayStepCount:^(double stepCount, NSError *error) {
yesterdayStepCount = stepCount;
if (yesterdayStepCount >0) {
queryResultBlock(yesterdayStepCount,nil);
}else{
queryResultBlock(-1,error);
}
}];
} else {
double stepCount = [result.sumQuantity doubleValueForUnit:[HKUnit countUnit]];
if(stepCount > 0){
if (queryResultBlock) {
queryResultBlock(stepCount,nil);
}
} else {
queryResultBlock(-1,error);
}
}
}];
[self.healthStore executeQuery:query];
}
#pragma mark - GetYesterdayStepCount
- (void)getYesterdayStepCount:(void (^)(double stepCount, NSError *error))queryResultBlock
{
HKQuantityType *quantityType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKStatisticsQuery *query = [[HKStatisticsQuery alloc]initWithQuantityType:quantityType quantitySamplePredicate:[self predicateForSamplesYesterday] options:HKStatisticsOptionCumulativeSum completionHandler:^(HKStatisticsQuery * _Nonnull query, HKStatistics * _Nullable result, NSError * _Nullable error) {
if (error) {
queryResultBlock(-1,error);
} else {
double stepCount = [result.sumQuantity doubleValueForUnit:[HKUnit countUnit]];
NSLog(@"当天行走步数 = %lf",stepCount);
if (queryResultBlock) {
queryResultBlock(stepCount,nil);
}
}
}];
[self.healthStore executeQuery:query];
}
#pragma mark - 获取协处理器步数
- (void)getCMPStepCount:(void(^)(double stepCount, NSError *error))completion
{
if ([CMPedometer isStepCountingAvailable] && [CMPedometer isDistanceAvailable]) {
if (!_pedometer) {
_pedometer = [[CMPedometer alloc]init];
}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
// 开始时间
NSDate *startDate = [calendar dateFromComponents:components];
// 结束时间
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
[_pedometer queryPedometerDataFromDate:startDate toDate:endDate withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
if (error) {
if(completion) completion(0 ,error);
} else {
double stepCount = [pedometerData.numberOfSteps doubleValue];
if(completion)
completion(stepCount ,error);
}
[self->_pedometer stopPedometerUpdates];
}];
}
}
//
-(NSDictionary *)collectCurrentHeartRateSample:(NSMutableArray *)currentSampleType{
NSDictionary *message = [NSDictionary dictionary];
self.currentHeartRateSample = currentSampleType;
self.currentHeartLastSample = self.currentHeartRateSample.lastObject;
if (self.currentHeartLastSample != nil) {
HKQuantitySample *lastHeartRateSample = (HKQuantitySample *)self.currentHeartRateSample.lastObject;
self.currentHeartRateBPM =[lastHeartRateSample.quantity doubleValueForUnit:[HKUnit unitFromString:@"count/min"]];
NSDate *heartRateStartDate = lastHeartRateSample.startDate;
NSDate *heartRateEndDate = lastHeartRateSample.endDate;
message = @{@"HeartRateBPM":[NSNumber numberWithDouble:self.currentHeartRateBPM],@"HeartRateStartDate":heartRateStartDate,@"HeartRateEndDate":heartRateEndDate};
}
return message;
}
#pragma mark - GetUserHeartRate (传入起止时间)返回一个时间段内的心率数组
- (void)fetchUserHeartRateWithStartTime:(NSInteger)startTime endTime:(NSInteger)endTime andCompletion:(void(^)(NSArray *value, NSError *error))completion
{
HKQuantityType *heartRate = [HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
NSSortDescriptor *timeSortDescriptor = [[NSSortDescriptor alloc] initWithKey:HKSampleSortIdentifierEndDate ascending:YES];
NSMutableArray *mArray = [NSMutableArray array];
NSDate *startDate = [NSDate dateWithTimeIntervalSince1970:startTime];
NSDate *endDate = [NSDate dateWithTimeIntervalSince1970:endTime];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
HKSampleQuery *query = [[HKSampleQuery alloc]initWithSampleType:heartRate predicate:predicate limit:HKObjectQueryNoLimit sortDescriptors:@[timeSortDescriptor] resultsHandler:^(HKSampleQuery * _Nonnull query, NSArray<__kindof HKSample *> * _Nullable results, NSError * _Nullable error) {
if(error) {
completion(@[],error);
} else {
for (HKSample *sample in results) {
HKQuantitySample *heartRateSample = (HKQuantitySample *)sample;
NSInteger heart = [heartRateSample.quantity doubleValueForUnit:[HKUnit unitFromString:@"count/min"]];
[mArray addObject:[NSString stringWithFormat:@"%ld",heart]];
}
completion([mArray copy],error);
}
}];
[self.healthStore executeQuery:query];
}
#pragma mark - isHealthDataAvailable
- (BOOL)isHealthDataAvailable
{
return [HKHealthStore isHealthDataAvailable];
}
#pragma mark - lazyLoad
- (HKHealthStore *)healthStore
{
if (!_healthStore) {
_healthStore = [[HKHealthStore alloc]init];
}
return _healthStore;
}
#pragma mark - 今天
- (NSPredicate *)predicateForSamplesToday {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:now];
[components setHour:0];
[components setMinute:0];
[components setSecond: 0];
NSDate *startDate = [calendar dateFromComponents:components];
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
return predicate;
}
#pragma mark -昨天
- (NSPredicate *)predicateForSamplesYesterday{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDate *lastDay = [NSDate dateWithTimeInterval:-24*60*60 sinceDate:now];
NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:lastDay];
[components setHour:0];
[components setMinute:0];
[components setSecond: 0];
NSDate *startDate = [calendar dateFromComponents:components];
NSDate *endDate = [calendar dateByAddingUnit:NSCalendarUnitDay value:1 toDate:startDate options:0];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionNone];
return predicate;
}
@end
备注:HealthKit授权只是单个项目的,比如步数是步数的授权,心率是心率的授权,因为每次传入的type值是不一样的
tips:
授权页面有 allow 和 notallow的只会弹出一次,如不允许后,下次想打开授权就必须进入到设置页面
//跳转到健康数据设置页面
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"x-apple-health://app/"] options:@{} completionHandler:^(BOOL success) {
}];
授权页面,写数据关闭时候,读数据打开,其实相当于未授权;
授权页面,写数据打开时候,读数据关闭,其实相当于无数据;
未完待续。。。