iOS Programming · · 26 min read

Introduction to HealthKit with Core Bluetooth

Introduction to HealthKit with Core Bluetooth

In my last Core Bluetooth tutorial, I discussed the technology, applications, and benefits of Apple’s Core Bluetooth framework. I showed you how to build an app that wirelessly connected to a Bluetooth® heart rate monitor (HRM) — a small, portable, and “wearable” device. My sample app’s code read heart rate data from the HRM, but it didn’t do anything with the heart rate data except display it in real-time on an iPhone’s screen. While edifying and interesting, there are already a bazillion apps in the App Store that can read heart rate data, and other wearable data, using Core Bluetooth. What about doing something interesting with that data, like analyzing it, deriving value-added and meaningful results from it, and sharing it with researchers? That’s where Apple’s HealthKit framework shines.

In this tutorial, I’m going to walk you through several essential HealthKit concepts:

  • Explain the concepts behind HealthKit.
  • Show you how HealthKit can give your career a boost.
  • Discuss the privacy implications of developing apps that can read/write a user’s very personal health data.
  • Show you how to prepare an Xcode project for integration with HealthKit.
  • Walk you through some Swift 4 code I wrote for reading from and writing to the HealthKit data store.
  • Show you the output from my code as it works with health data.
  • And, finally, give some hints as where you can go next with HealthKit.

Please follow and read the hyperlinks I’ve included throughout the article. This is important information required for developers to fully understand how HealthKit works and to fully understand how Apple takes privacy concerns very seriously when manipulating users’ health data.

What is HealthKit?

Isn’t that kind of an obvious question to answer? The HealthKit framework can collect, store, and organize all sorts of data regarding a person’s physical, mental, and even arguably spiritual state. The Health app you all see on your iPhones is Apple’s portal to the framework, but developers can take advantage of the HealthKit API to create their own health-related apps. Some data tracked by HealthKit can only be entered by the user, and is read-only to developers (like date of birth). Other data, like a user’s heart rate and blood pressure, can be collected and written into the HealthKit by developers — and they can read that data, too. In fact, once an app stores data in HealthKit, that data is available to all other apps, but only if the user consents on an app-by-app and health-data-point-by-data-point basis. Users have very granular control over their health data. Let me be emphatic: You can only access HealthKit data with the user’s consent.

You must keep users’ health data private

Before submitting a HealthKit app to the App Store, remember to follow all of Apple’s guidelines, i.e., “You must also provide a privacy policy for any app that uses the HealthKit framework.”

What’s all this privacy stuff? Think about it: Would you want someone you didn’t know, and without your knowledge, collecting all sorts of personal information about you, like your sexual activities, your blood alchohol content, your age, your average heart rate…? Ah… no. Information is power.

Just consider the ethical considerations of having access to peoples’ health data. Would it be moral to take advantage of their personal data? Not from my standpoint. But what if you provided a means for them to specifically control all of their health data, and only with their full knowledge and consent, willfully provide access to some or all of that data? HealthKit provides a firewall for Apple customers to have that level of control:

Because health data can be sensitive, HealthKit grants users control over their data by providing fine-grained control over the information that apps can share. The user must explicitly grant each app permission to read and write data to the HealthKit store. Users can grant or deny permission separately for each type of data. For example, a user could let your app read the step count data but prevent it from reading the blood glucose level. To prevent possible information leaks, an app does not know whether it has been denied permission to read data. From the app’s point of view, if the app has been denied permission to read data, no data of that type exists. …

The HealthKit data is only kept locally on the user’s device. For security, the HealthKit store is encrypted when the device is locked, and the HealthKit store can only be accessed by an authorized app. …

It’s not just ethics and morality that drives these privacy control mechanisms, there’s a law that is now a household word. With the proliferation of personal health data collection mechanisms, be they paper or electronic, the United States Congress passed a law governing privacy for health records in 1996, the Health Insurance Portability and Accountability Act of 1996 (HIPAA).

HealthKit can help your career

Just as I discussed how Core Bluetooth can advance your career, think of adding proficiency in HealthKit to your resume. The two technologies are practically married in the realm of health. Look at what I accomplished in my last tutorial and this one. I can read heart rate data from any Bluetooth® Low Energy HRM, and write it to and read it from the HKHealthStore. Do you know how much incredibly meaningful information can be gleaned from a user’s heart rate? If you can figure out how to add support, say, for a Bluetooth® blood pressure monitor and Bluetooth® pulse oximeter, and then combine the data from those two devices with heart rate data to derive some meaningful health metric, then the sky’s the limit.

The money to hire developers for health-related apps and health-device-combination apps is out there. According to PRNewswire:

The wearable medical devices market is expected to reach USD 14.41 billion by 2022 from USD 6.22 billion in 2017, at a CAGR [compound annual growth rate] of 18.3%. The key factors driving the growth of this market include increasing penetration of smartphones and growing number of smartphone-based healthcare apps compatible with wearable devices and growing preference for wireless connectivity among healthcare providers. …

According to Visiongain:

A new report by Visiongain predicts the global medical devices market will reach $398.0bn in 2017. That industry generated $321.0bn in 2012, and its revenues will show strong growth to 2023. …

That’s a lot of money. Good apps are developed by good companies who hire talented and passionate developers. The money’s there to hire you if you have the right skills. If you like to go it alone and have that entrepreneurial spirit, go for it. The money’s there for you, too.

When building apps, make sure you don’t make any claims that require U.S. Food and Drug Administration (FDA) approval — unless you’ve got the time and resources to go through the medical device approval process. Don’t make claims about your app that aren’t backed up by some hard, scientific research. But don’t get scared and run away either.

You’ll find it a lot easier to hire on with a company handling the science and the regulations than you would going it alone as a single app developer. But, hey, I work for myself and do just fine.

Writing the Code

In this discussion, I’m making the assumption that you understand the basics of iOS app development, including the Swift programming language and an Xcode Single View App template. In this tutorial’s code, I use no user interface (UI). I only show console output. HealthKit is so big and overwhelming that I want you to concentrate on the Swift code and HealthKit SDK API’s classes, methods, properties, and constants. The user interface (UI), including Auto Layout, for my “Core Bluetooth HRM” app, whose code is shown in my previous tutorial, and to which I’ve added the code shown below, is trivial.

I’ll first have to describe the steps required to configure an iOS Xcode project for integration with Apple’s HealthKit. Only then can I show you how to write Swift 4 code for reading from and writing to the HealthKit data store.

I’m going to extend the project I explained in my tutorial on Core Bluetooth. Remember that that code read heart rate data from a wearable — an HRM — and displayed it on screen. That app, named “Core Bluetooth HRM,” didn’t save any of the heart rate data collected from the HRM. We’ll do that in this tutorial. Please remember the app name, “Core Bluetooth HRM,” as we’re going to see it pop up in important places.

Preparing the app in Xcode

Because of HIPAA and other privacy considerations, Apple requires that developers jump through some hoops in Xcode before reading and writing HealthKit data, and rightly so, as we discussed previously.

• Add the HealthKit capability to the app.

Click on the project name in the Project Navigator, click on the target name, click on the Capabilities tab, and then switch HealthKit to the “ON” position:

Enable HealthKit Capability

• You must be a member of the Apple Developer Program to use HealthKit.

That means having provisioning profiles, development certificates, distribution certificates, among other things, but in this case, especially a development team. If you haven’t set up Signing in your app, you’ll get the following message after turning HealthKit to the “ON” position:

You could click the “Fix Issues” button here, but I want to edify you on the app signing process.

• Turn on code signing.

Click on the project name in the Project Navigator, click on the target name, click on the General tab, and expand the Signing section.

Tick the checkbox labelled “Automatically manage signing.” Notice the error message below as we’re not quite finished:

Assign Team

Select one of your valid Apple Developer Program account’s development teams from the “Team” dropdown/combobox.

• Make sure your app’s “Bundle Identifier” agrees with your Apple Developer Program account’s preferred corporate Internet domain.

You’ll find the Xcode Identity section ust above the Signing section we just discussed. Apple — and you — should take integrity seriously when using peoples’ health data. You need to show that your app is indeed your app.

In the “Bundle Identifier” field, make sure you prefix the app ID with your reversed domain name. According to Apple:

A bundle ID uniquely identifies a single app throughout the system. The bundle ID string must be a uniform type identifier (UTI) that contains only alphanumeric characters (A-Z,a-z,0-9), hyphen (-), and period (.). The string should be in reverse-DNS format. Bundle IDs are case sensitive.

In other words, if your company’s website is “http://joseph.com,” the “Bundle Identifier” for this app should be as shown here:

• During development, your device must trust the Apple ID associated with your Apple Developer Program account.

Remember I’m adding HealthKit support to my “Core Bluetooth HRM” app. I also played with the Apple “Fit” demo app. When first installing both apps on my devices from Xcode, I received the following error message:

Do what the previously-shown message box says. Settings should now look like this:

• There’s one final step, and my app will crash without it:

In your app’s info.plist, set the NSHealthShareUsageDescription key to customize the message for reading data. Set the NSHealthUpdateUsageDescription key to customize the message for writing data.

Whenever you even think about reading from or writing to HealthKit, you ask the user for authorization using built-in “permissions sheets.” These sheets will contain the custom messages from the Info.plist file. Here’s a snippet from mine:

...
    NSHealthShareUsageDescription
    Reason my app reads your health data
    NSHealthUpdateUsageDescription
    Reason my app makes changes to your health data
...

You’ll want to use more precise explanations in a real app. I’ll show you examples of permissions sheets below.

The Simple Scenario: Reading one fixed value with Swift 4

Let’s start with reading one HealthKit fixed “characteristic,” a value that can only be written by the user and only read by an app, if the user consents.

I’m going to describe the code in a series of steps — steps that are also comments in my code shown below. So as you read the steps in this section, please refer to those same steps in the code below. The setup process is basically linear, but reading from the HealthKit data store is asynchronous. Reads have completion blocks which run on background queues. If you want to update the UI from a completion handler, you better jump back onto the main thread. For tutorials on completion blocks, see here and here. For jumping onto the main thread, see here.

I’ve chosen to wrap all HealthKit functionality for my “Core Bluetooth HRM” app in a class, HealthKitInterface.

The code steps in English
Step 1: Import the HealthKit framework. That gives me access to HealthKit symbols that “are declared outside the current file.”

Step 2: Create a member property to store an instance of HKHealthStore, the “access point for all data managed by HealthKit.”

Step 3: Create a member property that we’ll use to ask HealthKit about the user’s gender. An HKCharacteristicType represents data the user enters themselves during setup of the iOS Health app. I’m deliberately starting with a fixed characteristic to differentiate from the process of reading or writing information like heart rate data, of which I can collect numerous samples, and read samples from devices or from HealthKit itself to perform custom analysis. I can also collect heart rate samples from a device or app and write those into HealthKit. From Apple:

Unlike the other object types, characteristic types cannot be used to create and save new HealthKit objects. Instead, users must enter and edit their characteristic data using the Health app. Similarly, you cannot create queries for characteristic types. Instead, use the HealthKit store to access the data (see Reading Characteristic Data).

Step 4: For flexibility, the HealthKit API allows us to ask about multiple characteristics at once. So I create a Set as specified by the API as you’ll see below.

Step 5: Make sure HealthKit is available because “If HealthKit is not available on the device (for example, on an iPad), … HealthKit methods fail…”

Step 6: Create one instance of the HealthKit data store. It’s the conduit to all health data. Read the docs: “You need only a single HealthKit store per app. These are long-lived objects. Create the store once, and keep a reference for later use.”

Step 7: I create a Set of one HKCharacteristicType as that’s what the next call, requestAuthorization, wants, so it knows what data I’m asking for permission to access. See next step.

Step 8: This step is where all the action in this part of my app happens. First, I ask the user for permission to read their gender type. Once I get permission from the user, I … go to Step 9.1 below. Read the doc on requestAuthorization(toShare:read:completion:). The permission request is asynchronous.

Step 9.1 I read the gender characteristic data by calling by calling my API wrapper method readGenderType(). I’m doing so in the completion handler for requestAuthorization. Remember that the completion block is called on a background queue, so be sure to jump back on the main thread if you want to update the UI with the user’s gender here.

Step 9.2 My API wrapper method readGenderType() takes care of the details surrounding the actual HKHealthStore call to biologicalSex().

Seeing is believing – Simple: Reading one fixed value with Swift 4

How do I kick off the code I just explained above? What happens when I kick off the code I just explained above?. Well, here you go… I’ll show you what the app does when run. Then I’ll show you the code.

What the app looks like
The user first sees a HealthKit screen asking to give permission to my app, “Core Bluetooth HRM,” to read gender:

This is a permissions sheet as I discussed above.

If the user 1) pushes the UISwitch for “Sex” to the “ON” position and then 2) presses the “Allow” button, I get HealthKit authorization just to read gender, I read it, and write it to the console as shown here:

The initialization code
This is the high-level code in my “Core Bluetooth HRM” app that leverages my core HealthKitInterface class. I declare an instance variable of my HealthKitInterface type in my UIViewController subclass, HeartRateMonitorViewController. Then I ask for permission to read gender and read that value if the user consents. This is all done in the init() for class HealthKitInterface. It’s pretty straightforward. This snippet is a modified version of the code from my last tutorial on Core Bluetooth.

class HeartRateMonitorViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
...
    // HealthKit setup
    let healthKitInterface = HealthKitInterface()
...

The core HealthKit code for a single read
I added a Swift file to my “Core Bluetooth HRM” app named HealthKitInterface.swift. It contains my HealthKitInterface class. Remember to review the steps I explained above when reading my corresponding inline comments in the code below:

import Foundation

// STEP 1: MUST import HealthKit
import HealthKit

class HealthKitInterface
{
    
    // STEP 2: a placeholder for a conduit to all HealthKit data
    let healthKitDataStore: HKHealthStore?
    
    // STEP 3: get a user's physical property that won't change
    let genderCharacteristic = HKCharacteristicType.characteristicType(forIdentifier: HKCharacteristicTypeIdentifier.biologicalSex)
    
    // STEP 4: for flexibility, the API allows us to ask for
    // multiple characteristics at once
    let readableHKCharacteristicTypes: Set?
    
    init() {
        
        // STEP 5: make sure HealthKit is available
        if HKHealthStore.isHealthDataAvailable() {
            
            // STEP 6: create one instance of the HealthKit store
            // per app; it's the conduit to all HealthKit data
            self.healthKitDataStore = HKHealthStore()
            
            // STEP 7: I create a Set of one as that's what the call wants
            readableHKCharacteristicTypes = [genderCharacteristic!]
            
            // STEP 8: request user permission to read gender and
            // then read the value asynchronously
            healthKitDataStore?.requestAuthorization(toShare: nil,
                                                     read: readableHKCharacteristicTypes,
                                                          completion: { (success, error) -> Void in
                                                            if success {
                                                                print("Successful authorization.")
                                                                // STEP 9.1: read gender data (see below)
                                                                self.readGenderType()
                                                            } else {
                                                                print(error.debugDescription)
                                                            }
                                                    })
            
        } // end if HKHealthStore.isHealthDataAvailable()
            
        else {
            
            self.healthKitDataStore = nil
            readableHKCharacteristicTypes = nil
            
        }
        
    } // end init()
    
    // STEP 9.2: actual code to read gender data
    func readGenderType() -> Void {
        
        do {
            
            let genderType = try self.healthKitDataStore?.biologicalSex()
            
            if genderType?.biologicalSex == .female {
                print("Gender is female.")
            }
            else if genderType?.biologicalSex == .male {
                print("Gender is male.")
            }
            else {
                print("Gender is unspecified.")
            }
            
        }
        catch {
            print("Error looking up gender.")
        }
        
    } // end func readGenderType
    
} // end class HealthKitInterface

The Complex Scenario: Reading and writing samples with Swift 4

Let’s explore a more complex scenario. I’m going to develop code that writes data to and reads data from the HKHealthStore. This is more complex data than gender (just one value of .female, .male, or .unspecified) that is maintained solely by the user. I can collect thousands of samples of heart rate data values and store them in the HKHealthStore. Heart rate samples can be collected from multiple devices and/or apps besides my own. I can read all heart rate data or just a specific date range from the HKHealthStore. Heart rate has a measurement unit (beats per minute). I’ll have to write some code to accomodate this more complex data type.

I’m going to describe the code in a series of steps — steps that are also comments in my code shown below. So as you read the steps in this section, please refer to those same steps in the code below. The setup process is basically linear, but reading from and writing to the HealthKit data store is asynchronous. Reads and writes have completion blocks which run on background queues. If you want to update the UI from a completion handler, you better jump back onto the main thread. For tutorials on completion blocks, see here and here. For jumping onto the main thread, see here.

I’ve chosen to wrap all HealthKit functionality for my “Core Bluetooth HRM” app in a class, HealthKitInterface.

The code steps in English
Step 1: Import the HealthKit framework. That gives me access to HealthKit symbols that “are declared outside the current file.”

Step 2: Create a member property to store an instance of HKHealthStore, the “access point for all data managed by HealthKit.”

Step 3: Create member properties that we’ll use to ask HealthKit about heart rate data. We use the HKQuantityType, which are “Quantity types [that] identify samples that store numerical values” like heart rate samples. These are types of data that can change, be collected in large quantities, and for which we can define our own sample types.

For flexibility, the HealthKit API allows us to ask about multiple HKQuantityTypes at once. So I create a Set each for reading and writing as specified by the API as you’ll see below. Note that I could’ve used one property, but I used two for semantic clarity (i.e., reading is different than writing).

Step 4: Make sure HealthKit is available because “If HealthKit is not available on the device (for example, on an iPad), … HealthKit methods fail…”

Step 5: Create one instance of the HealthKit data store. It’s the conduit to all health data. Read the docs: “You need only a single HealthKit store per app. These are long-lived objects. Create the store once, and keep a reference for later use.”

Step 6: I create two Sets of HKQuantityTypes as that’s what the next call, requestAuthorization, wants, so it knows what data I’m asking for permission to access. See next step. Notice that HKQuantityTypeIdentifier.heartRate is one of the predefined types already known to HealthKit.

Step 7: I ask the user for permission to read and write their heart rate data. Once I get permission from the user, I configure my code to write some heart rate data, and then I configure it to read the most recently-stored heart rate data. Read the doc on requestAuthorization(toShare:read:completion:). The permission request is asynchronous.

Step 8.0: This method is my wrapper for writing one heart rate sample at a time to the HKHealthStore. We’re building a specification to tell the HKHealthStore precisely what data and meta data we’re writing into it.

Step 8.1: The HKUnit.count() definition: “Count units are used to represent raw scalar values. They are often used to represent the number of times an event occurs…” like heart beats per minute.

Step 8.2: HKQuantity: “HealthKit uses quantity objects to store numerical data. When you create a quantity, you provide both the quantity’s value and unit.” Read this line of code. You should be able to tell that it’s taking a numeric heart rate measurement and is specifying it with context:

beats per minute = heart rate / minute

Step 8.3: Using HKQuantityType.quantityType is the final step is specifying a heart rate in beats per minute for storage in the HKHealthStore. From the doc I linked to: “HealthKit uses quantity types to create samples that store a numerical value. Use quantity type instances to create quantity samples that you can save in the HealthKit store.”

Step 8.4: HKQuantitySample: “you can use a quantity sample to record … the user’s current heart rate…” I’ve got my heart rate measurement with context ready for storage. By setting both the start and end dates to the same date/time stamp, I’m recording one heart rate measurement at one point in time.

Step 8.5: HKHealthStore.save: I’m saving an array of one heart rate measurement with a single date/time stamp to the HKHealthStore. This is an asynchronous call and the completion block is invoked on a background queue. If you’re going to update the UI in the completion block, you better jump onto the main thread.

Step 9.0: This is my wrapper method for reading all “recent” heart rate samples from the HKHealthStore. Think about this in terms of a database query that may take awhile to complete based on how you fine-tune the WHERE clause.

Step 9.1: Just as in Step 6 above, we’re telling the HealthKitStore that we’re interested in reading — querying for — heart rate data.

Step 9.2: HKAnchoredObjectQuery is a “query that returns only recent changes to the HealthKit store; this query both returns a snapshot of new changes, and can continue to monitor the store as a long-running query.” To keep this tutorial from drowning you in details, I’ve created a query against HealthKitStore that is constrained only to looking for “recent” heart rate data. Notice that I’ve set no predicate, no anchor, and no limit. If you think about it in pseudo-SQL, the query is akin to:

SELECT bpm FROM HealthKitStore WHERE qtyTypeID = '.heartRate';

Click on HKAnchoredObjectQuery to get an overview and detailed instructions for limiting the scope of your queries — and how to continually “monitor the store as a long-running query.”

Step 9.3: Execute the query for heart rate data.

Seeing is believing – Complex: Reading and writing samples with Swift 4

How do I kick off the code I just explained above? What happens when I kick off the code I just explained above?. Well, here you go… I’ll show you what the app does when run. Then I’ll show you the code.

What the app looks like
The user first sees a HealthKit screen asking to give permission to my app, “Core Bluetooth HRM,” to read and write their heart rate data:

The user must slide both UISwitch controls to the “ON” position and then press the “Allow” button.

I first record a few seconds worth of heart rate data. Remember that these data samples will be associated with my “Core Bluetooth HRM” app since it’s communicating with an HRM — and I’m wearing that HRM. Here’s some console output during this recording of HRM data and writing it to the HKHealthStore:

...
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 72 saved.
BPM is UInt8
Heart rate 61 saved.
BPM is UInt8
Heart rate 60 saved.
BPM is UInt8
Heart rate 60 saved.
BPM is UInt8
Heart rate 60 saved.
BPM is UInt8
Heart rate 61 saved.
BPM is UInt8
Heart rate 61 saved.
BPM is UInt8
Heart rate 62 saved.
BPM is UInt8
Heart rate 62 saved.
BPM is UInt8
Heart rate 62 saved.
BPM is UInt8
Heart rate 62 saved.
BPM is UInt8
Heart rate 62 saved.
BPM is UInt8
Heart rate 62 saved.
BPM is UInt8
Heart rate 62 saved.
BPM is UInt8
Heart rate 62 saved.
...

I can use my iPhone to look at the data I just recorded using my HRM and “Core Bluetooth HRM” app. Remember my app wrote to the HKHealthStore. I just use Apple’s Health app, go to its Health Data tab, then Heart table view cell, pick one of the big red squares labelled Heart Rate, and then click its (i) button. Then I click the Show All Data table view cell:

Notice that each of these samples is tagged with its source being my “Core Bluetooth HRM” app.

I then configure my “Core Bluetooth HRM” app to read the most recent heart rate data that’s been written to the HKHealthStore using a “query.” I write its output to the console. This query runs for awhile. You can see what I mean here:

Notice there’s some data in there from my Apple Watch. Eventually, the data written using my “Core Bluetooth HRM” app gets read. Here’s the console output from the read query:

...
60 count/min BB3810FA-9C35-40D7-A427-91989567BD95 "Andrew’s Apple Watch" (4.2), "Watch3,2" (4.2)"Apple Watch" metadata: {
    HKMetadataKeyHeartRateMotionContext = 1;
} (2018-04-13 19:27:59 -0600 - 2018-04-13 19:27:59 -0600)

68 count/min 096B69F5-1B91-40AF-8370-D4347AEDB7DA "Andrew’s Apple Watch" (4.2), "Watch3,2" (4.2)"Apple Watch" metadata: {
    HKMetadataKeyHeartRateMotionContext = 1;
} (2018-04-13 19:43:34 -0600 - 2018-04-13 19:43:34 -0600)

72 count/min E39086F0-4B3B-498B-8401-C7F232F5E053 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:35 -0600 - 2018-04-13 20:06:35 -0600)

72 count/min BD02CEE4-FDB5-44EC-A5A4-FD60333C5417 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:36 -0600 - 2018-04-13 20:06:36 -0600)

72 count/min 765DB416-B60A-4825-A62B-CC31A90FD024 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:37 -0600 - 2018-04-13 20:06:37 -0600)

72 count/min D74E2A55-C0D5-4744-BF54-770294B61A94 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:38 -0600 - 2018-04-13 20:06:38 -0600)

72 count/min 995F38E6-767C-4648-842A-6E9CA179304A "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:39 -0600 - 2018-04-13 20:06:39 -0600)

72 count/min EBCACCB8-809E-4E24-8727-46C580653FCE "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:40 -0600 - 2018-04-13 20:06:40 -0600)

72 count/min C2E6FECC-1DB6-42ED-B47C-0F18F51D16A6 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:41 -0600 - 2018-04-13 20:06:41 -0600)

72 count/min B9E0B283-8AB6-4E0C-BF54-D624A66DA17F "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:42 -0600 - 2018-04-13 20:06:42 -0600)

72 count/min 003D8E16-1FB7-4355-9CF6-F0052190A7E7 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:43 -0600 - 2018-04-13 20:06:43 -0600)

72 count/min 61DAED07-37B7-40F9-B1C8-A7B7C9817E8B "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:44 -0600 - 2018-04-13 20:06:44 -0600)

72 count/min 170613A8-7F70-4346-B72E-5B1E03F559F6 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:45 -0600 - 2018-04-13 20:06:45 -0600)

61 count/min 906F7194-DE87-4742-A8ED-C13701D45E49 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:46 -0600 - 2018-04-13 20:06:46 -0600)

60 count/min 296E78E1-4E25-4B5D-83E9-9D34A72FB315 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:47 -0600 - 2018-04-13 20:06:47 -0600)

60 count/min EB46B4AD-DAE7-4CF4-AEE1-A8F3F3E02A41 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:48 -0600 - 2018-04-13 20:06:48 -0600)

60 count/min 388E598E-B593-40DF-A3A4-86964829A1B1 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:49 -0600 - 2018-04-13 20:06:49 -0600)

61 count/min 1095509E-70AC-4783-A275-A02B38CBE099 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:50 -0600 - 2018-04-13 20:06:50 -0600)

61 count/min A21AA698-94E6-4F2B-A349-F9D57A904C9D "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:51 -0600 - 2018-04-13 20:06:51 -0600)

62 count/min 5E4D5CA9-8D25-40E7-956A-82D2CCE72B58 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:52 -0600 - 2018-04-13 20:06:52 -0600)

62 count/min 7101BF55-69D2-48F6-B4C7-EC5C9CDAB0C7 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:53 -0600 - 2018-04-13 20:06:53 -0600)

62 count/min 77F4143B-2F8B-414F-8D91-1C1BC7865C8F "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:54 -0600 - 2018-04-13 20:06:54 -0600)

62 count/min 01C7D0B7-EE9B-46F8-89BE-52DC552470AD "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:55 -0600 - 2018-04-13 20:06:55 -0600)

62 count/min 490365CC-D618-467E-8B8B-2FAE81D1B1BD "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:56 -0600 - 2018-04-13 20:06:56 -0600)

62 count/min 8FBD601D-833F-49BA-B9FF-78541B1513CE "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:57 -0600 - 2018-04-13 20:06:57 -0600)

62 count/min B52AA49B-9494-4B7F-9F2D-C411C09EF739 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:58 -0600 - 2018-04-13 20:06:58 -0600)

62 count/min 2AFD1DC8-87FE-4F61-B4F3-3FD9D71601A6 "Core Bluetooth HRM" (1), "iPhone9,2" (11.2.1) (2018-04-13 20:06:59 -0600 - 2018-04-13 20:06:59 -0600)

69 count/min B82D8FEA-09EF-4CDD-91F1-4ADDD9B87BF7 "Andrew’s Apple Watch" (4.2), "Watch3,2" (4.2)"Apple Watch" metadata: {
    HKMetadataKeyHeartRateMotionContext = 1;
} (2018-04-13 19:49:01 -0600 - 2018-04-13 19:49:01 -0600)

74 count/min B023CE6F-3DFC-4857-B79E-9CDE5ED21A21 "Andrew’s Apple Watch" (4.2), "Watch3,2" (4.2)"Apple Watch" metadata: {
    HKMetadataKeyHeartRateMotionContext = 1;
} (2018-04-13 19:53:58 -0600 - 2018-04-13 19:53:58 -0600)
...

The initialization code
This is the high-level code that leverages my core HealthKitInterface class in my “Core Bluetooth HRM” app. First, I’ll write a few seconds of heart rate samples to the HKHealthStore using my “Core Bluetooth HRM” app:

class HeartRateMonitorViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
...
    // HealthKit setup
    let healthKitInterface = HealthKitInterface()
...
    func deriveBeatsPerMinute(using heartRateMeasurementCharacteristic: CBCharacteristic) -> Int {
        
        let heartRateValue = heartRateMeasurementCharacteristic.value!
        let buffer = [UInt8](heartRateValue)

        if ((buffer[0] & 0x01) == 0) {
            print("BPM is UInt8")
            
            // store heart rate data in HKHealthStore
            healthKitInterface.writeHeartRateData(heartRate: Int(buffer[1]))
            
            return Int(buffer[1])
        } else { 
            print("BPM is UInt16")
            return -1
        }
        
    } // END func deriveBeatsPerMinute
...

Then I’ll read the most recent heart rate samples from the HKHealthStore using my “Core Bluetooth HRM” app. All’s I need to do is 1) comment out the call to healthKitInterface.writeHeartRateData in method deriveBeatsPerMinute and 2) call healthKitInterface.readHeartRateData in the viewDidLoad of the HeartRateMonitorViewController class:

...
    override func viewDidLoad() {
        super.viewDidLoad()
...
        // read heart rate data from HKHealthStore
        healthKitInterface.readHeartRateData()
    }
...

The core HealthKit code for a multiple reads and writes
Here’s the meat of my HealthKit code for reading and writing data to the HealthKitStore. This code requests user authorization to health data, writes heart rate samples to the HealthKitStore, and reads heart rate samples from the HealthKitStore.

import Foundation

// STEP 1: MUST import HealthKit
import HealthKit

class HealthKitInterface
{
    
    // STEP 2: a placeholder for a conduit to all HealthKit data
    let healthKitDataStore: HKHealthStore?
    
    // STEP 3: create member properties that we'll use to ask
    // if we can read and write heart rate data
    let readableHKQuantityTypes: Set?
    let writeableHKQuantityTypes: Set?
    
    init() {
        
        // STEP 4: make sure HealthKit is available
        if HKHealthStore.isHealthDataAvailable() {
            
            // STEP 5: create one instance of the HealthKit store
            // per app; it's the conduit to all HealthKit data
            self.healthKitDataStore = HKHealthStore()
            
            // STEP 6: create two Sets of HKQuantityTypes representing
            // heart rate data; one for reading, one for writing
            readableHKQuantityTypes = [HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!]
            writeableHKQuantityTypes = [HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!]
            
            // STEP 7: ask user for permission to read and write
            // heart rate data
            healthKitDataStore?.requestAuthorization(toShare: writeableHKQuantityTypes,
                                                     read: readableHKQuantityTypes,
                                                         completion: { (success, error) -> Void in
                                                            if success {
                                                                print("Successful authorization.")
                                                            } else {
                                                                print(error.debugDescription)
                                                            }
                                                    })
            
        } // end if HKHealthStore.isHealthDataAvailable()
            
        else {
            
            self.healthKitDataStore = nil
            self.readableHKQuantityTypes = nil
            self.writeableHKQuantityTypes = nil
            
        }
        
    } // end init()
    
    // STEP 8.0: this is my wrapper for writing one heart
    // rate sample at a time to the HKHealthStore
    func writeHeartRateData( heartRate: Int ) -> Void {
        
        // STEP 8.1: "Count units are used to represent raw scalar values. They are often used to represent the number of times an event occurs"
        let heartRateCountUnit = HKUnit.count()
        // STEP 8.2: "HealthKit uses quantity objects to store numerical data. When you create a quantity, you provide both the quantity’s value and unit."
        // beats per minute = heart beats / minute
        let beatsPerMinuteQuantity = HKQuantity(unit: heartRateCountUnit.unitDivided(by: HKUnit.minute()), doubleValue: Double(heartRate))
        // STEP 8.3: "HealthKit uses quantity types to create samples that store a numerical value. Use quantity type instances to create quantity samples that you can save in the HealthKit store."
        // Short-hand for HKQuantityTypeIdentifier.heartRate
        let beatsPerMinuteType = HKQuantityType.quantityType(forIdentifier: .heartRate)!
        // STEP 8.4: "you can use a quantity sample to record ... the user's current heart rate..."
        let heartRateSampleData = HKQuantitySample(type: beatsPerMinuteType, quantity: beatsPerMinuteQuantity, start: Date(), end: Date())
        
        // STEP 8.5: "Saves an array of objects to the HealthKit store."
        healthKitDataStore?.save([heartRateSampleData]) { (success: Bool, error: Error?) in
            print("Heart rate \(heartRate) saved.")
        }
        
    } // end func writeHeartRateData
    
    // STEP 9.0: this is my wrapper for reading all "recent"
    // heart rate samples from the HKHealthStore
    func readHeartRateData() -> Void {

        // STEP 9.1: just as in STEP 6, we're telling the `HealthKitStore`
        // that we're interested in reading heart rate data
        let heartRateType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
        
        // STEP 9.2: define a query for "recent" heart rate data;
        // in pseudo-SQL, this would look like:
        //
        // SELECT bpm FROM HealthKitStore WHERE qtyTypeID = '.heartRate';
        let query = HKAnchoredObjectQuery(type: heartRateType, predicate: nil, anchor: nil, limit: HKObjectQueryNoLimit) {
            (query, samplesOrNil, deletedObjectsOrNil, newAnchor, errorOrNil) in
            
            if let samples = samplesOrNil {

                for heartRateSamples in samples {
                    print(heartRateSamples)
                }
                
            } else {
                print("No heart rate sample available.")
            }
            
        }

        // STEP 9.3: execute the query for heart rate data
        healthKitDataStore?.execute(query)
        
    } // end func readHeartRateData
 
} // end class HealthKitInterface

Conclusion

I hope you’ve enjoyed this tutorial on Apple technology for health-related apps. Buy or borrow a Bluetooth® Low Energy device and use my code with it or write your own. Follow all the hyperlinks I’ve provided in the article and read them. You should definitely study Apple’s HealthKit documentation at this link, this link and this link. Download and practice with Apple’s “Fit” demo app. It’s written in Objective-C, but you’re all adaptable people. I like Objective-C.

Once proficient in HealthKit and/or Core Bluetooth, broaden your horizons by looking into Apple’s ResearchKit and CareKit. Apple says that:

ResearchKit is a powerful tool that helps medical researchers gather health data from many iPhone users. You can take part and allow your data to be used in meaningful studies. And CareKit helps you take an active role in managing your own health, giving you tools to track things like your symptoms and medications, then share that information with your care team.

Thanks for tuning in. Remember to enjoy your work. Don’t forget that having HealthKit (and bonus prize Bluetooth®) experience on your resume will be a big plus for your career.

For reference, you can check out the full source code on GitHub.

Read next