The Most Straight Forward Tutorial on How to Use HealthKit for SwiftUI

Retrieve step count from today & current week in 4 simple steps.

Kevin Bryan
6 min readJun 1, 2023

On my last project, I created a step count tracker app. Therefore, I used Apple’s HealthKit to retrieve their step count information.

Here is how I implemented HealthKit on my project! 👇

1. Enable HealthKit Capability

Add HealthKit by clicking +Capability

First step is going to Signing & Capabilities by clicking your app target and adding HealthKit by searching it when you click on +Capability.

Add the Health Key to the Info List

Then you should add the necessary key to your Info list. The 2 keys usually used are Privacy — Health Share Usage Description (to read data from HealthKit) & Health Update Usage Description (to update data to HealthKit). Since, I’m only reading the data, I’ll only be using the Share Usage Description.

Don’t forget to add concrete reason in the Value column why you are using those keys, such as: To read the health data and show it in the application.

2. Create a HealthKit Manager

To make your code maintainable and clean, let’s create a HealthKit Manager. This class is responsible for retrieving and updating data to the HealthKit.

The HealthKit Manager will interact using HKHealthStore() to interact with the HealthKit. Let’s also prepare stepCountToday and thisWeekSteps to store the step count we will be fetching.

import Foundation
import HealthKit
import WidgetKit

class HealthKitManager: ObservableObject {
static let shared = HealthKitManager()

var healthStore = HKHealthStore()

var stepCountToday: Int = 0
var thisWeekSteps: [Int: Int] = [1: 0, 2: 0, 3: 0,
4: 0, 5: 0, 6: 0, 7: 0]
}

Next, don’t forget to add HealthKitManager to the View that needs data from the HealthKit.


@StateObject var healthKitManager = HealthKitManager.shared

3. Request User’s Health Authorization

Next is creating a function to request User’s authorization to use their Health data for our application. In this step you prepare a set of datatype you read and write, an example can be seen in the toReads.

Here I only prepare toReads since I am not updating any data to HealthKit.

  init() {
requestAuthorization()
}


func requestAuthorization() {
// this is the type of data we will be reading from Health (e.g stepCount)
let toReads = Set([
HKObjectType.quantityType(forIdentifier: .stepCount)!,

// this is to make sure User's Heath Data is Avaialble
guard HKHealthStore.isHealthDataAvailable() else {
print("health data not available!")
return
}

// asking User's permission for their Health Data
// note: toShare is set to nil since I'm not updating any data
healthStore.requestAuthorization(toShare: nil, read: toReads) {
success, error in
if success {
self.fetchAllDatas()
} else {
print("\(String(describing: error))")
}
}
}
A page similar to this will then appear in your Content View

4. Fetching & Showing The Data

Now let’s get to the fun part. Let’s create the function to fetch the stepCount data. I will show you how to fetch the stepCount for Today and for This Week.

Here are the things to keep in mind when fetching data from HealthKit:

  • The data type you are fetching using from HealthKit.
guard let stepCountType = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
return
}
  • The startDate and endDate for the range of date you are fetching your data from.
let now = Date()
let startDate = Calendar.current.startOfDay(for: now)

// here I am using the start of today as startDate, and now as endDate
  • After preparing the startDate and endDate you can then prepare a predicate using HKSampleQuery.
let predicate = HKQuery.predicateForSamples(
withStart: startDate,
end: now,
options: .strictStartDate
)
  • Next is preparing the query, where you combine the data type, predicate, and add an option. Here is where you will also store the data to the variable set before hand.
let query = HKStatisticsQuery(
quantityType: stepCountType, // the data type
quantitySamplePredicate: predicate, // the predicate using the set startDate and endDate
options: .cumulativeSum // to get the total steps
) {
_, result, error in
guard let result = result, let sum = result.sumQuantity() else {
print("failed to read step count: \(error?.localizedDescription ?? "UNKNOWN ERROR")")
return
}

let steps = Int(sum.doubleValue(for: HKUnit.count()))
self.stepCountToday = steps
}
  • Finally, you should run healthStore.execute(query).
healthStore.execute(query)

Here is how the complete function to fetch Today’s stepCount looks like:

  func readStepCountToday() {
guard let stepCountType = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
return
}

let now = Date()
let startDate = Calendar.current.startOfDay(for: now)
let predicate = HKQuery.predicateForSamples(
withStart: startDate,
end: now,
options: .strictStartDate
)

let query = HKStatisticsQuery(
quantityType: stepCountType,
quantitySamplePredicate: predicate,
options: .cumulativeSum
) {
_, result, error in
guard let result = result, let sum = result.sumQuantity() else {
print("failed to read step count: \(error?.localizedDescription ?? "UNKNOWN ERROR")")
return
}

let steps = Int(sum.doubleValue(for: HKUnit.count()))
self.stepCountToday = steps
}
healthStore.execute(query)
}

and here is the code to get this week’s stepCount:

func readStepCountThisWeek() {
guard let stepCountType = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
return
}
let calendar = Calendar.current
let today = calendar.startOfDay(for: Date())

// Find the start date (Monday) of the current week
guard let startOfWeek = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: today)) else {
print("Failed to calculate the start date of the week.")
return
}

// Find the end date (Sunday) of the current week
guard let endOfWeek = calendar.date(byAdding: .day, value: 6, to: startOfWeek) else {
print("Failed to calculate the end date of the week.")
return
}

let predicate = HKQuery.predicateForSamples(
withStart: startOfWeek,
end: endOfWeek,
options: .strictStartDate
)

let query = HKStatisticsCollectionQuery(
quantityType: stepCountType,
quantitySamplePredicate: predicate,
options: .cumulativeSum, // fetch the sum of steps for each day
anchorDate: startOfWeek,
intervalComponents: DateComponents(day: 1) // interval to make sure the sum is per 1 day
)

query.initialResultsHandler = { _, result, error in
guard let result = result else {
if let error = error {
print("An error occurred while retrieving step count: \(error.localizedDescription)")
}
return
}

result.enumerateStatistics(from: startOfWeek, to: endOfWeek) { statistics, _ in
if let quantity = statistics.sumQuantity() {
let steps = Int(quantity.doubleValue(for: HKUnit.count()))
let day = calendar.component(.weekday, from: statistics.startDate)
self.thisWeekSteps[day] = steps
}
}
}

healthStore.execute(query)
}
}

For the weekly stepCount function, we enumerate through each day of the week from Sunday to Saturday, and set the data to the initial dictionary we prepared.

Finally, I experimented with SwiftUI and this is the final UI of the app.

The Final User Interface
  • Based on my experience, getting into HealthKit can be cumbersome at first, especially seeing how long the code looks at first glance.
  • However, after much trial and error, HealthKit is actually quite simple and easy to understand.
  • You can first start by exploring the WWDC video and documentation first like I did and then move on to tutorials done by other developers.
  • Side not, in this tutorial, I tried my best to kept the code clean by having my own HealthKitManager class.
  • For me, the WWDC HealthKit video helped a ton to understand how the HealthKit API works in general, so if you are still confused, you can give that video a shot.

Thanks for reading 😁 I hope this article helps! 🙌

If you would like to see the full code, please refer to the GitHub repo: https://github.com/kevicebryan/NC2-iWalker

--

--