Blog How To: Integrate Your Heart Rate App with HealthKit API

At the Apple Worldwide Developer Conference earlier this month Apple introduced an app simply called “Health”that promises to become an all-embracing hub for healthcare and fitness applications and wearable devices. It will allow you to store all of your health data and vital signs in one place including your heart rate, blood pressure, blood sugar, sleep patterns, consumed and burnt calories and more. It will also give you control over all of your health apps through one interface.

The news inspired our team to integrate our health apps with the HealthKit API to help bring the world one step closer to integrated health data. We started with our Heartbeat Rate application that processes the video stream from your iPhone/ iPod camera to measure heart rate. It analyzes the bluecomponent in the video stream providing reliable results, thus no physical contact is required. (Read more about Heartbeat Rate application.)

Here is the recap of how our Heartbeat rate app was integrated with HealthKit API.

In our application, the main screen provides for heart rate measurement.
In the view controller measurement start time is stored in NSDate *startDate.
The value of the measurement end time is stored in NSDate *endDate.
Average heart rate value (beats per minute) is saved in averageCollector.average and is a double type variable.

Our aim was to ensure that the value was added to HKHealthStore upon the press of the UIButton on the main screen. The view controller code would be as follows:

@implementation HBViewController
 
...
 
- (IBAction)tapHKLogB:(UIButton *)b {
    double beatsPerMinute = averageCollector.average;
    [[HKManager sharedManager]
     storeHeartBeatsAtMinute:beatsPerMinute
     startDate:startDate endDate:endDate
     completion:^(NSError *error) {
         if(error) {
             UIAlertView *av = [UIAlertView alertWithTitle:@"HealthStore"
                                                   message:error.hkManagerErrorMessage];
             [av addButtonWithTitle:@"Cancel"];
             [av addButtonWithTitle:@"Repeat" handler:^{
                 [self tapHKLogB:b];
             }];
             [av show];
         } else {
             [averageCollector removeAllDoubles];
             [self updateLogUI];
 
             NSString *message = [NSString stringWithFormat:@"%@ B/m have been logged!", @((int)beatsPerMinute)];
             UIAlertView *av = [UIAlertView alertWithTitle:@"HealthStore"
                                                   message:message];
             [av addButtonWithTitle:@"Ok"];
             [av show];
         }
     }];
}
 
...
 
@end

This typical action is trying to record the average rate over a given period of time. When there’s a positive outcome, the app pops up a success message and clears the accumulated results. When there’s a failure, an error message pops up suggesting the user tries again. I should add that this code would also require some progress indicator, as the process might take up to 10 seconds (iPod Touch 5 gen, iOS 8 Beta 2).

Before we start considering HKManager, I’d like to note that I extended the NSError class slightly so that reporting HKManager-specific errors would be more convenient. Namely, I added a constructor and a getter to receive error summary text. Here’s how it looks:

@interface NSError (HKManager)
 
@property (readonly) NSString *hkManagerErrorMessage;
+ (NSError *)hkManagerErrorWithMessage:(NSString *)errorMessage;
 
@end

NSError (HKManager) implementation:

static NSString *kHKManagerErrorDomain = @"HKManagerErrorDomain";
static NSString *kHKManagerErrorMessageKey = @"HKManagerErrorMessageKey";
 
@implementation NSError (HKManager)
- (NSString *)hkManagerErrorMessage {
    return self.userInfo[kHKManagerErrorMessageKey];
}
+ (NSError *)hkManagerErrorWithMessage:(NSString *)errorMessage {
    return
    [NSError errorWithDomain:kHKManagerErrorDomain code:0 userInfo:@{kHKManagerErrorMessageKey: errorMessage}];
}
@end

Now, let’s take a look at what HKManager looks like.

HKManager is a singleton that coordinates its operation with HKHealthStore

@interface HKManager : NSObject {
    HKHealthStore *store;
}
 
+ (instancetype)sharedManager;
 
- (void)authorizeWithCompletion:(void (^)(NSError *error))completion;
- (void)storeHeartBeatsAtMinute:(double)beats
                     startDate:(NSDate *)startDate endDate:(NSDate *)endDate
                    completion:(void (^)(NSError *error))completion;
 
@end

Method + (instancetype)sharedManager; is necessary to create our singleton

Method – (void)authorizeWithCompletion:(void (^)(NSError *error))completion; is essential to authorize our application to add samples to Health App storage.

It can be done in AppDelegate as follows:

@implementation HBAppDelegate
 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
 
    [[HKManager sharedManager] authorizeWithCompletion:^(NSError *error) {
        UIAlertView *av = [UIAlertView alertWithTitle:@"HealthKit" message:error.hkManagerErrorMessage];
        [av addButtonWithTitle:@"OK"];
        [av show];
    }];
 
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.viewController = [[HBViewController alloc] initWithNibName:@"HBViewController" bundle:nil];
    self.window.rootViewController = self.viewController;
 
    [self.window makeKeyAndVisible];    
    return YES;
}
 
@end

During the app authorization process a modal dialog appears.

authoriz

After we give our app permission to record heart rhythm, the source should appear in Health app.

sourses

What does authorizeWithCompletion: look like?

- (void)authorizeWithCompletion:(void (^)(NSError *error))completion {
    if(![HKHealthStore isHealthDataAvailable]) {
        if(completion) {
            completion([NSError hkManagerErrorWithMessage:@"This device does not support HealthKit"]);
        }
        return;
    }
 
    if(!store) {
        store = [HKHealthStore new];
    }
 
    [store requestAuthorizationToShareTypes:[self shareTypes]
                                  readTypes:[self readTypes] completion:^(BOOL success, NSError *error) {
                                      if(error && completion) {
                                          completion([NSError hkManagerErrorWithMessage:error.localizedDescription]);
                                      }
                                  }];
}

First, it sends a request to HKHealthStore to see if HKHealthStore is available on the device (e.g. on iPad it’s currently unavailable). Then, it requests authorization with write permissions HKQuantityTypeIdentifierHeartRate.

- (NSSet *)shareTypes {
    return [NSSet setWithObject:[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate]];
}

 

And an empty set of read permissions:

- (NSSet *)readTypes {
    return [NSSet set];
}

Now, let’s consider the process of adding samples.

Method

(void)storeHeartBeatsAtMinute:(double)beats
                     startDate:(NSDate *)startDate endDate:(NSDate *)endDate
                    completion:(void (^)(NSError *error))completion;

adds quantities for beats for a period of time from startDate to endDate.

- (void)storeHeartBeatsAtMinute:(double)beats
                      startDate:(NSDate *)startDate endDate:(NSDate *)endDate
                     completion:(void (^)(NSError *error))completion
{
    HKQuantityType *rateType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
    HKQuantity *rateQuantity = [HKQuantity quantityWithUnit:[HKUnit heartBeatsPerMinuteUnit]
                                                doubleValue:(double)beats];
    HKQuantitySample *rateSample = [HKQuantitySample quantitySampleWithType:rateType
                                                                   quantity:rateQuantity
                                                                  startDate:startDate
                                                                    endDate:endDate];
 
    [store saveObject:rateSample withCompletion:^(BOOL success, NSError *error) {
        if(completion) {
            completion(error);
        }
    }];
}

First it creates quantity type:

  HKQuantityType *rateType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];

Then, it creates the quantity itself with the given unit of measurement [HKUnit heartBeatsPerMinuteUnit] and the beats value:

HKQuantity *rateQuantity = [HKQuantity quantityWithUnit:[HKUnit heartBeatsPerMinuteUnit]
                                                doubleValue:(double)beats];

[HKUnit heartBeatsPerMinuteUnit] isn’t part of HealthKit. It’s a custom HKUnit extention:

@interface HKUnit (HKManager)
+ (HKUnit *)heartBeatsPerMinuteUnit;
@end
 
@implementation HKUnit (HKManager)
 
+ (HKUnit *)heartBeatsPerMinuteUnit {
    return [[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]];
}
 
@end

Next we create a sample with a given quantity and a given period of time:

       HKQuantitySample *rateSample = [HKQuantitySample quantitySampleWithType:rateType
                                                                   quantity:rateQuantity
                                                                  startDate:startDate
                                                                    endDate:endDate];

And finally we add the sample to HKHealthStore:

[store saveObject:rateSample withCompletion:^(BOOL success, NSError *error) {
        if(completion) {
            completion(error);
        }
    }];

These are the results of three measurements taken within an hour.

dashboard

HKManager source code can be found here.

Download

DataArt is a custom software development firm that builds advanced solutions for the financial services,healthcare, hospitality, IoT/M2M and media & entertainment industries. Also, DataArt leads a number of R&D projects that allow the company to provide their clients high-end software products. More information about DataArt is available on the site http://dataart.com.


Subscribe to our news


2 thoughts on “How To: Integrate Your Heart Rate App with HealthKit API”

Leave a Reply