HealthKit Setup
Configuring HealthKit
Enable HealthKit capability in Xcode and add the required Info.plist keys.
- Add HealthKit capability in Xcode
- Add
NSHealthShareUsageDescriptionandNSHealthUpdateUsageDescriptionto 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?
2. What must you check before using HealthKit?
3. How do you request access to health data?
4. What type represents a workout in HealthKit?
Flashcards
Question
What is HKHealthStore?
Click to reveal answer
Answer
The main class for reading and writing health data in the HealthKit framework.
Question
How do you read step count from HealthKit?
Click to reveal answer
Answer
Use HKStatisticsQuery with HKQuantityType for .stepCount and a date predicate.
Question
What are the two types of authorization in HealthKit?
Click to reveal answer
Answer
Write (toShare) and Read authorization for specific health data types.
Question
What is an anchored query used for?
Click to reveal answer
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