Skip to content
advanced Phase 8 · Media & Hardware

HealthKit

Read and write health data, request permissions, and build fitness-related features.

50m
2 problems
Topic Progress 0%

HealthKit Setup

Configuring HealthKit

Enable HealthKit capability in Xcode and add the required Info.plist keys.

  1. Add HealthKit capability in Xcode
  2. Add NSHealthShareUsageDescription and NSHealthUpdateUsageDescription to Info.plist

HKHealthStore Initialization

Create an HKHealthStore instance to interact with the Health app.

import HealthKit

class HealthManager {
    static let shared = HealthManager()
    let healthStore = HKHealthStore()
    
    func requestAuthorization() async throws {
        guard HKHealthStore.isHealthDataAvailable() else {
            throw HealthError.notAvailable
        }
        
        let readTypes: Set<HKSampleType> = [
            HKQuantityType.quantityType(forIdentifier: .stepCount)!,
            HKQuantityType.quantityType(forIdentifier: .heartRate)!,
            HKObjectType.workoutType()
        ]
        
        let writeTypes: Set<HKSampleType> = [
            HKQuantityType.quantityType(forIdentifier: .stepCount)!,
            HKObjectType.workoutType()
        ]
        
        try await healthStore.requestAuthorization(toShare: writeTypes, read: readTypes)
    }
}

Checking HealthKit Availability

Always verify HealthKit is available before accessing it.

func checkHealthKit() {
    if HKHealthStore.isHealthDataAvailable() {
        print("HealthKit is available")
    } else {
        print("HealthKit not available on this device")
    }
}

Reading Health Data

Fetching Quantity Samples

Read step count, heart rate, and other quantity data from HealthKit.

import HealthKit

extension HealthManager {
    func fetchStepCount(for date: Date) async throws -> Double {
        let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
        let predicate = HKQuery.predicateForSamples(
            withStart: Calendar.current.startOfDay(for: date),
            end: date,
            options: .strictStartDate
        )
        
        let steps = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Double, Error>) in
            let query = HKStatisticsQuery(
                quantityType: stepType,
                quantitySamplePredicate: predicate,
                options: .cumulativeSum
            ) { _, result, error in
                if let error = error {
                    continuation.resume(throwing: error)
                    return
                }
                let count = result?.sumQuantity()?.doubleValue(for: .count()) ?? 0
                continuation.resume(returning: count)
            }
            healthStore.execute(query)
        }
        return steps
    }
}

Fetching Workout Samples

Retrieve completed workouts from HealthKit.

extension HealthManager {
    func fetchWorkouts(startDate: Date, endDate: Date) async throws -> [HKWorkout] {
        let workoutType = HKObjectType.workoutType()
        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate)
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
        
        return try await withCheckedThrowingContinuation { continuation in
            let query = HKSampleQuery(
                sampleType: workoutType,
                predicate: predicate,
                limit: HKObjectQueryNoLimit,
                sortDescriptors: [sortDescriptor]
            ) { _, results, error in
                if let error = error {
                    continuation.resume(throwing: error)
                    return
                }
                let workouts = results as? [HKWorkout] ?? []
                continuation.resume(returning: workouts)
            }
            healthStore.execute(query)
        }
    }
}

Observing Health Data Changes

Use an observer query to get notified when health data changes.

func observeStepChanges() {
    let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
    let query = HKObserverQuery(sampleType: stepType, predicate: nil) { _, completionHandler, error in
        if error == nil {
            NotificationCenter.default.post(name: .stepCountUpdated, object: nil)
        }
        completionHandler()
    }
    healthStore.execute(query)
    healthStore.enableBackgroundDelivery(for: stepType, frequency: .hourly) { _, _, _ in }
}

Writing Health Data

Saving Quantity Samples

Write step counts, distances, and other metrics to HealthKit.

extension HealthManager {
    func saveSteps(count: Double, date: Date) async throws {
        let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
        let quantity = HKQuantity(unit: .count(), doubleValue: count)
        let sample = HKQuantitySample(
            type: stepType,
            quantity: quantity,
            start: date,
            end: date
        )
        
        try await healthStore.save(sample)
        print("Steps saved successfully")
    }
}

Saving Workouts

Create and save workout sessions with associated metrics.

extension HealthManager {
    func saveWorkout(type: HKWorkoutActivityType, startDate: Date, endDate: Date, calories: Double) async throws {
        let workout = HKWorkout(
            activityType: type,
            start: startDate,
            end: endDate,
            duration: endDate.timeIntervalSince(startDate),
            totalEnergyBurned: HKQuantity(unit: .kilocalorie(), doubleValue: calories),
            totalDistance: nil,
            metadata: nil
        )
        
        try await healthStore.save(workout)
        print("Workout saved successfully")
    }
}

Anchored Queries for Incremental Updates

Use anchored queries to efficiently fetch only new or deleted samples.

extension HealthManager {
    func fetchIncrementalSteps(anchor: HKQueryAnchor?) async throws -> (samples: [HKQuantitySample], newAnchor: HKQueryAnchor) {
        let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
        
        return try await withCheckedThrowingContinuation { continuation in
            let query = HKAnchoredObjectQuery(
                type: stepType,
                predicate: nil,
                anchor: anchor,
                limit: HKObjectQueryNoLimit
            ) { _, samples, _, newAnchor, error in
                if let error = error {
                    continuation.resume(throwing: error)
                    return
                }
                let quantitySamples = samples as? [HKQuantitySample] ?? []
                continuation.resume(returning: (quantitySamples, newAnchor))
            }
            healthStore.execute(query)
        }
    }
}

Quiz

1. What class manages interactions with the Health app?

Question 1 options

2. What must you check before using HealthKit?

Question 2 options

3. How do you request access to health data?

Question 3 options

4. What type represents a workout in HealthKit?

Question 4 options

Flashcards

Question

What is HKHealthStore?

Answer

The main class for reading and writing health data in the HealthKit framework.

Question

How do you read step count from HealthKit?

Answer

Use HKStatisticsQuery with HKQuantityType for .stepCount and a date predicate.

Question

What are the two types of authorization in HealthKit?

Answer

Write (toShare) and Read authorization for specific health data types.

Question

What is an anchored query used for?

Answer

Fetching incremental updates - only new or deleted samples since the last anchor point.

Revision Notes

Key Takeaways

  • 1. Always check HKHealthStore.isHealthDataAvailable() first
  • 2. Request specific read and write authorizations
  • 3. Use async/await for modern HealthKit queries
  • 4. Anchored queries are efficient for incremental updates
  • 5. Observer queries enable background data monitoring

Interview Tips

  • Explain the difference between read and write authorization
  • Discuss how to efficiently sync health data in the background
  • Describe the anchored query pattern for incremental updates

Cheat Sheet

HealthKit Quick Reference

  • HKHealthStore - Main store interface
  • requestAuthorization() - Request read/write permissions
  • HKQuantityType - Type for numeric health data
  • HKWorkout - Represents a workout session
  • HKStatisticsQuery - Aggregate data queries
  • HKAnchoredObjectQuery - Incremental updates
  • HKObserverQuery - Data change notifications
  • .stepCount, .heartRate - Common identifiers