Accessing Heart Rate Data for Your ResearchKit Study

In this tutorial, you’ll learn how to easily access heart rate data from HealthKit and use it in a ResearchKit study. By Matt Luedke.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Measuring Walking and Resting Heart Rate

ResearchKit includes a pre-made task to read health data while the user walks and rests for set periods of time. Active and resting heart rates hold key importance in heart studies, and now you can easily measure them.

Create a blank Swift file named WalkTask.swift and add the following:

import ResearchKit

public var WalkTask: ORKOrderedTask {
  return ORKOrderedTask.fitnessCheckTaskWithIdentifier("WalkTask",
    intendedUseDescription: nil,
    walkDuration: 15 as NSTimeInterval,
    restDuration: 15 as NSTimeInterval,
    options: .None)
}

Here, you create a new ORKOrderedTask, which you should find familiar after building several in the previous ResearchKit tutorial.

You create this particular task with fitnessCheckTaskWithIdentifier(_:intendedUseDescription:walkDuration:restDuration:options:), which you supply with a descriptive string identifier (WalkTask) and the durations (in seconds) of the walking and resting segments. ResearchKit will take it from there and create your task for you.

Next, open ViewController.swift and add the following IBAction:

@IBAction func walkTapped(sender: AnyObject) {
  let taskViewController = ORKTaskViewController(task: WalkTask, taskRunUUID: nil)
  taskViewController.delegate = self
  taskViewController.outputDirectory = NSURL(fileURLWithPath:
    NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0],
    isDirectory: true)
  presentViewController(taskViewController, animated: true, completion: nil)
  HealthKitManager.startMockHeartData()
}

Here, you create an ORKTaskViewController for your new fitness task, set this view controller as the delegate, specify an output directory for any result files (of which there will be several), and present the view controller. This should be familiar, as it’s how you presented MicrophoneTask in the previous ResearchKit tutorial.

The only new aspect is that after doing all this, you start mocking the heart data. And since you are starting the mock data, you should stop it as well.

Find taskViewController(_:didFinishWithReason:error:) – this is a ORKTaskViewControllerDelegate protocol method that gets called when taskViewController is dismissed. Stop the mock heart data at that point by adding the following line to the top of the method:

HealthKitManager.stopMockHeartData()

As you may recall from earlier, stopMockHeartData() stops the repeating timer you started to generate mock data every second.

Lastly, you need a UI trigger for this IBAction. Open Main.storyboard, add a UIButton entitled “Walk,” and connect it to walkTapped(_:).

WalkButton

Again, optionally add some Auto Layout constraints to align the button to the other buttons.

Build and run your app; tap on Walk and you’ll be led through a short exercise task, during which you’ll see your (unrealistically-widely varying) mock heart rate data:

Walk task

Note: If running in the simulator, you’ll see an error appear in the console around the time the Walk step begins, which looks similar to this:
WARNING: 997: Failure to setup sound, err = -50
This is because ResearchKit attempts to emit a vibration at the start and end of an active step, and fortunately for all of us, the simulator doesn’t vibrate! :]

You can complete the task now, but what happens to the results? Time to find them.

data_or_it_didnt_happen

After completing the walking task, you’ll receive an ORKTaskResult that contains the results of every step. You’ll get to that in a moment, but first you need to create something to parse that data. Create a new file named ResultParser.swift, and replace its contents with the following:

import Foundation
import ResearchKit

struct ResultParser {

  static func findWalkHeartFiles(result: ORKTaskResult) -> [NSURL] {

    var urls = [NSURL]()

    if let results = result.results
      where results.count > 4,
      let walkResult = results[3] as? ORKStepResult,
      let restResult = results[4] as? ORKStepResult {

      // TODO: find ORKFileResults
    }

    return urls
  }
}

findWalkHeartFiles(_:) accepts an ORKTaskResult that contains the results of every step in your walk task. Note as you are completing the walking test that the top of the walking step says Step 4 of 6; this is your sign that the fourth member – results[3] – holds the results for that step.

Likewise, the resting step is fifth and thus its results are at results[4]. At each of these positions is an ORKStepResult which itself may contain a number of ORKFileResult objects with the date for that step.

To parse out each ORKFileResult and find the file URL within, replace the following:

// TODO: find ORKFileResults

…with the code below:

for result in walkResult.results! {
  if let result = result as? ORKFileResult,
    let fileUrl = result.fileURL {
    urls.append(fileUrl)
  }
}

for result in restResult.results! {
  if let result = result as? ORKFileResult,
    let fileUrl = result.fileURL {
    urls.append(fileUrl)
  }
}

You cycle through the results for the two steps of interest and add the fileUrl of each result to an array. The fileUrl points to a JSON file containing the results of the associated step reading. In this case, these results will contain measurements for heart rate in BPM during both the walk and rest phases.

This may seem like a lot of nesting to find the data you like, but the system will become familiar once you get used to digging into the task results. You will see this again when you get the results of your own custom task.

This function takes in an ORKTaskResult and returns an array of file URLs, but where does the ORKTaskResult come from?

To find it, open ViewController.swift and locate taskViewController(_:didFinishWithReason:error:). Every ORKTaskViewController contains a result property – that’s what you want. Send it to your parser by adding the following code just below HealthKitManager.stopMockHeartData() at the top of the method:

if (taskViewController.task?.identifier == "WalkTask"
      && reason == .Completed) {

  let heartURLs = ResultParser.findWalkHeartFiles(taskViewController.result)

  for url in heartURLs {
    do {
      let string = try NSString.init(contentsOfURL: url, encoding: NSUTF8StringEncoding)
      print(string)
    } catch {}
  }
}

Here, you check to see if the task is indeed the WalkTask, and that it completed successfully. Then, you use your ResultParser to get the series of file URLs for the step results. Finally, you print the contents of each file to the console.

Build an run your app; tap the Walk button, complete the activity again, then tap the Done button. Look at the console in Xcode, and you’ll notice the printed output in JSON format. If you want to send the results to a server, you could do so without any further serialization!

Below is a small piece of data in the format you will see in your console:

{"items":[{"unit":"count\/min","type":"HKQuantityTypeIdentifierHeartRate","endDate":"2015-10-07T22:22:04-0700","source":"Karenina","value":138,"startDate":"2015-10-07T22:22:04-0700"},{"unit":"count\/min","type":"HKQuantityTypeIdentifierHeartRate","endDate":"2015-10-07T22:22:05-0700","source":"Karenina","value":129,"startDate":"2015-10-07T22:22:05-0700"}]}

As you can see, you have the value, units, and date of every heart rate reading. You’ll notice one array that correlates to the walk step, and another for the rest step – although you haven’t done anything here to flag which is which.

That’s it for the walking task. Now it’s time to build your own task.

Matt Luedke

Contributors

Matt Luedke

Author

Over 300 content creators. Join our team.