Use HealthKit to enable your iOS and watchOS apps to work with the Apple Health app.

All subtopics
Posts under Health and Fitness topic

Post

Replies

Boosts

Views

Activity

Wake-up detection with HealthKit
Hello HealthKit Experts & Enthusiasts! I am building an app called one sec which forces people to take a deep breath before they can use social media apps (it’s using Shortcuts Automations for that). One important feature of one sec is the Good Morning Countdown: For a specified time after waking up (e.g. 30mins) selected apps are blocked completely. This helps to start the day screen-free. They way it works is, the user grants access to read HKCategoryTypeIdentifier.sleepAnalysis data. I have implemented a HKObserverQuery and enableBackgroundDelivery in order to be informed whenever new HKCategoryTypeIdentifier.sleepAnalysis becomes available. I noticed that when I have my device connected to Xcode, this works as expected. However, when I quit the app and launch it from my Home Screen, my observer query is not informed about new sleep data (except when my app is running in foreground). Any ideas? Furthermore, I have noticed that sometimes sleep data is provided delayed to HealthKit, many minutes (sometimes even longer) after waking up, no sleep samples are to be found in the Health app. Of course, for my app it is crucial to get accurate + timely so apps can be blocked accordingly. Is this an issue that the Apple Watch first needs to send the samples to the phone? Thanks a lot for your help!
1
1
765
Sep ’24
recoverActiveWorkoutSession() inaccurate for paused sessions
When a workout session is being recovered, if it is paused, the elapsed time will be incorrect. It will seem like that workout never was paused. The recovery works fine if the workout was never paused. Steps to reproduce: Implement recoverActiveWorkoutSession Start workout Pause session and print the elapsed time Stop simulator / cause crash When recoverActiveWorkoutSession is called the elapsed time will not equal the elapsed time when the session was paused. Here is my implementation. I haven't seen any examples online. guard let recovered = try? await healthStore.recoverActiveWorkoutSession() else {return} self.session = recovered self.builder = recovered.associatedWorkoutBuilder() self.session?.delegate = self self.builder?.delegate = self self.builder?.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, workoutConfiguration: recovered.workoutConfiguration) self.sessionState = recovered.state
1
0
852
Sep ’24
Issue with Air play since new update iOs18 (apple fitness App)
Can’t start fitness video with my iPhone on my tv since new update 18.1. Air play is connecting I see the countdown then the dots rolling but nothing happens and I end up receiving a « can’t read video » message. I’m using an iphone 16 pro max, an apple watch ultra (1st gen) and an apple tv HD (A1625 model). I have almost the same issue with a more recent Apple TV, but on that one it’s just painfully long before the video starts but it does start eventually (just taking much longer than it used to). That some tv is an A2843 model (apple tv 4k 3rd gen). Has anyone else had this issue?
1
0
662
Oct ’24
Stands not detected
I have FB12696743 open since July 21, 2023 and this happened again today. I get home at approx 10 mins after the hour, walk appox 50 ft across my yard, up 5 steps into my house, let the dog out and pace on my deck watching the dog, go back in the house walk around the kitchen while preparing dinner. A total of about 200 ft. I sit down about 35 past the hour and start to eat and at 10 mins to the next our and I get the reminder to stand. On the other side I wake up at 5 mins to hour. Walk 8 steps to the bathroom and successfully achieve the stand for that hour. WHY!?!?!? 😁🤣
1
1
485
Jun ’25
HKWorkoutSession.sendToRemoteWorkoutSession doesn't report success or failure
We are seeing an issue where sending data using the asynchronous method HKWorkoutSession.sendToRemoteWorkoutSession(data: Data) will never return in some cases (no success nor failure). This issue is happening for roughly 5% of Workouts started and will stay broken for the whole workout. The other 95% of the workouts, the connection works flawlessly. This happens on both watchOS 10 and 11, and with phones running iOS 17 or 18. The issue is quite random and not reproducible. Our app has thousands of workouts a day that use the workout session workout data send, with constant messages being send every few seconds. In some of those 5% cases the "sendToRemoteWorkoutSession" will throw way later, like 30+ minutes later, if the watch app is awake long enough to capture a log of a failure. Our code uses the same flow as in the sample project: https://developer.apple.com/documentation/healthkit/workouts_and_activity_rings/building_a_multidevice_workout_app Here is some sample code, which is pretty simple. Setup code: let workoutSession = try HKWorkoutSession(healthStore: healthStore, configuration: configuration) workoutSession.delegate = self activeWorkoutSession?.startMirroringToCompanionDevice { success, error in print("Mirroring started on companion device: \(success), error: \(error)") } workoutSession?.prepare() then later we send data using the workout session: do { print("Will send data") try await workoutSession.sendToRemoteWorkoutSession(data: data) print("Successfully sent data") // This nor the error may be called after waiting extensive amounts of time } catch { print("Failed to send data, error: \(error)") // This nor the success may be called after waiting extensive amounts of time } So far, the only fix is to restart the phone and watch at the same time, which is not a great user experience. Is anyone else seeing this issue? or know how to fix this issue?
1
0
552
Mar ’25
Why do we still have the 7 day limitation of HealthKit data on Apple Watch?
It may have made sense in the early days of watchOS, but given the Apple Watch is now 10 years old and we have "Standalone" Apple Watch apps, it no longer makes sense to have this seemingly arbitrary limitation of only being able to query 7 days of data on the watch. I have an open feedback (FB7649612) from 2020 with no responses and ask this question every year at WWDC Developer labs. WHY must we still deal with this limitation which only causes other developers to store critical health data in iCloud or on their own servers in order to provide a robust stand alone watch experience on the Apple Watch. Even Apple themselves must either use a separate private API or use iCloud for the new Vitals app. How else can I escalate this request?
1
1
610
Dec ’24
HealthKit SDK Not Responding When Querying Step Data on iPhone 16 Pro Max
We have working code to fetch step data from HealthKit after requesting the necessary permissions. However, we’ve encountered an issue specific to one device, the iPhone 16 Pro Max. When querying the data, we do not receive a response, and the code enters an infinite loading state without completing the request. The user who is facing this issue has tried logging in on another device, and it works fine. On the problematic device (iPhone 16 Pro Max), the request does not complete. For reference, I’ve included the code below. Resolving this issue is crucial, so we would appreciate any guidance on what steps we can take to troubleshoot or resolve the problem on this specific device. Please note that the device has granted permission to access HealthKit data. static let healthStore = HKHealthStore() static func limitReadFromHealthKitBetweenDates(fromDate: Date, toDate: Date = Date(), completion: @escaping ([HKStatistics]) -> Void) { guard let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount) else { return } let ignoreUserEntered = HKQuery.predicateForObjects(withMetadataKey: HKMetadataKeyWasUserEntered, operatorType: .notEqualTo, value: true) let now = toDate var interval = DateComponents() interval.day = 1 var calendar = Calendar.current calendar.locale = Locale(identifier: "en_US_POSIX") var anchorComponents = calendar.dateComponents([.day, .month, .year], from: now) anchorComponents.hour = 0 let anchorDate = calendar.date(from: anchorComponents) ?? Date() let query = HKStatisticsCollectionQuery(quantityType: stepsQuantityType, quantitySamplePredicate: ignoreUserEntered, options: [.cumulativeSum], anchorDate: anchorDate, intervalComponents: interval) query.initialResultsHandler = { _, results, error in guard let results = results else { print("Error returned from resultHandler: \(String(describing: error?.localizedDescription))") return } print(results) var statisticsArray: [HKStatistics] = [] results.enumerateStatistics(from: fromDate, to: now) { statistics, _ in statisticsArray.append(statistics) if statistics.endDate.getddmmyyyyslashGMT == now.getddmmyyyyslashGMT { completion(statisticsArray) } } } healthStore.execute(query) } Please note that the code works on all devices except the problematic one. Could you please guide me on the next steps to resolve this issue?
1
0
901
Dec ’24
Change displayed metric in Fitness app
Good afternoon, I am working on a workout tracking app. So far everything is working as expected. However, I note that when my workout saves and is visible within the Fitness App, the workout duration is displayed rather than the kCal burned. What changes are required to be made in order for this to display the kCal in the list of workouts in Fitness rather than duration? For reference https://developer.apple.com/videos/play/wwdc2021/10009 this was my reference source for workout functionality.
1
0
905
Jan ’25
Workout not showing for import on Strava
I have a workout app which I am testing on device currently via TestFlight. The generated workout (tennis and indoor) shows in the fitness app with correct HR and duration. However, when I go to my Strava app, it does not show in the list of workouts for importing. (note, activities tracked using the regular tennis mode on the Apple Watch show fine) I have also concurrently reached out to Strava support to see if there's anything they can offer support for. However, does anybody here have any knowledge/experience of the requirement? Or whether this is a limitation of an application deployed via TestFlight? I have a terrible feeling I am chasing ghosts, and it may be a TestFlight limitation for exporting workouts? Thanks
1
0
874
Jan ’25
Inquiry About Background Permission Issue in My App
I am writing to address a concern regarding the background permission functionality in my app, which is critical for ensuring user safety as they navigate various terrains. This feature also enables users to smoothly record their navigation tracks for review after their activities. Recently, I've noticed that this functionality is not working as seamlessly as before. Additionally, I observed that the app is not categorized under 'health and fitness'—could reclassifying it improve background activity? Before I delve into a detailed code review, I wanted to check if this issue might be related to sync or settings on the App Store side, such as permission configurations, app updates, or other related factors. Or, is it more likely an issue stemming from the app’s codebase?
1
0
441
Jan ’25
HealthKit permissions not honoring user selection
I'm dealing with a strange bug where I am requesting read access for 'appleExerciseTime' and 'activitySummaryType', and despite enabling both in the permission sheet, they are being set to 'sharingDenied'. I'm writing a Swift Test for making sure permissions are being granted. @Test func PermissionsGranted() { try await self.manager.getPermissions() for type in await manager.allHealthTypes { let status = await manager.healthStore.authorizationStatus(for: type) #expect(status == .sharingAuthorized, "\(type) authorization status is \(status)") } } let healthTypesToShare: Set<HKSampleType> = [ HKQuantityType(.bodyMass), HKQuantityType(.bodyFatPercentage), HKQuantityType(.leanBodyMass), HKQuantityType(.activeEnergyBurned), HKQuantityType(.basalEnergyBurned), HKObjectType.workoutType() ] let allHealthTypes: Set<HKObjectType> = [ HKQuantityType(.bodyMass), HKQuantityType(.bodyFatPercentage), HKQuantityType(.leanBodyMass), HKQuantityType(.activeEnergyBurned), HKQuantityType(.basalEnergyBurned), HKQuantityType(.appleExerciseTime), HKObjectType.activitySummaryType() ] let healthStore = HKHealthStore() func getPermissions() async throws { try await healthStore.requestAuthorization(toShare: self.healthTypesToShare, read: self.allHealthTypes) } After 'getPermissions' runs, the permission sheet shows up on the Simulator, and I accept all. I've double checked that the failing permissions show up on the sheet and are enabled. Then the test fails with: Expectation failed: (status → HKAuthorizationStatus(rawValue: 1)) == (.sharingAuthorized → HKAuthorizationStatus(rawValue: 2)) HKActivitySummaryTypeIdentifier authorization status is HKAuthorizationStatus(rawValue: 1) Expectation failed: (status → HKAuthorizationStatus(rawValue: 1)) == (.sharingAuthorized → HKAuthorizationStatus(rawValue: 2)) HKActivitySummaryTypeIdentifier authorization status is HKAuthorizationStatus(rawValue: 1) With the rawValue of '1' being 'sharingDenied'. All other permissions are granted. Is there a workaround here, or something I'm potentially doing wrong?
1
0
949
Jan ’25
Can't properly check if Health Kit is authorized.
In my WatchOS app I've written the following code to check if my app has access to the user's health data: func isHealthKitAuthorized() -> Bool { let typesToRead: [HKObjectType] = [ HKObjectType.quantityType(forIdentifier: .heartRate)!, HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!, HKObjectType.quantityType(forIdentifier: .appleMoveTime)!, HKObjectType.quantityType(forIdentifier: .appleExerciseTime)!, HKObjectType.workoutType() ] let typesToShare: Set<HKSampleType> = [ HKObjectType.workoutType(), HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!, HKObjectType.quantityType(forIdentifier: .heartRate)! ] var isAuthorized = true for type in typesToRead { let status = healthStore.authorizationStatus(for: type) if status != .sharingAuthorized { print("Access denied to: \(type.identifier)") isAuthorized = false } } for type in typesToShare { let status = healthStore.authorizationStatus(for: type) if status != .sharingAuthorized { print("Access denied to: \(type.identifier)") isAuthorized = false } } return isAuthorized } However for the appleMoveTime and appleExerciseTime types their status is returning as 'sharingDenied' (checked by printing the status' rawValue) even though they are authorized on the Watch's settings. This happened both on the simulator and on the Watch itself. Am I doing something wrong?
1
0
803
Jan ’25
How to Save Heart Rate in HKCategoryTypeIdentifier.mindfulSession
I’m trying to associate heart rate (HR) data with a mindfulness session (HKCategoryTypeIdentifier.mindfulSession) in HealthKit, but I can’t find any documentation on how to do this. I’ve seen third-party apps (like Medito) successfully log HR within Mindful Minutes, even when the session takes place on an iPhone (not an Apple Watch). However, when I try saving HR in the metadata, it does not appear in the Health app's Mindful Minutes section. Code snippet: func logMindfulnessSession(start: Bool, heartRate: Double? = nil) { let mindfulType = HKCategoryType.categoryType(forIdentifier: .mindfulSession)! let now = Date() let endTime = now.addingTimeInterval(Double(selectedDuration)) var metadata: [String: Any]? = nil if let hr = heartRate { let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute()) let hrQuantity = HKQuantity(unit: heartRateUnit, doubleValue: hr) metadata = ["heartRate": hrQuantity] // ❓ Is there a correct key for HR? } let sample = HKCategorySample( type: mindfulType, value: 0, start: now, end: endTime, metadata: metadata ) healthStore.save(sample) { success, error in if let error = error { print("HealthKit session save error: \(error.localizedDescription)") } else { print("Mindfulness session saved successfully.") if let hr = heartRate { print("Saved with HR: \(hr) BPM") } } } } Questions: What is the correct metadata key for associating heart rate with a mindful session? Does HealthKit require a specific format (e.g., HKQuantitySample) for HR? 0 Are there additional permissions needed to allow HR to appear in Mindful Minutes? Does HR need to be stored separately in HKQuantityTypeIdentifier.heartRate, and if so, how do third-party apps ensure it appears in the same entry as the mindful session? thank you!
1
0
583
Mar ’25
HKAnchoredObjectQuery Stops Receiving Updates
I implemented this to receive updates for specific data types and keep the latest daily information up to date. However, for some reason, it only works for a while before stopping completely. Background Delivery internal func backgroundDeliveryForReadTypes(enable: Bool, types: Set<HKQuantityType>) async { do { if enable { try await statusForAuthorizationRequest(toWrite: [], toRead: types) for type in types { try await healthStore.enableBackgroundDelivery(for: type, frequency: .daily) } } else { for type in types { try await healthStore.disableBackgroundDelivery(for: type) } } } catch { debugPrint("Error enabling background delivery: \(error.localizedDescription)") } } HKQueryAnchor internal var walkingActivityQueryAnchor: HKQueryAnchor? { get { if let anchorData = UserDefaults.standard.data(forKey: "walkingActivityAnchor") { return try? NSKeyedUnarchiver.unarchivedObject(ofClass: HKQueryAnchor.self, from: anchorData) } return nil } set { if let newAnchor = newValue { let anchorData = try? NSKeyedArchiver.archivedData(withRootObject: newAnchor, requiringSecureCoding: true) UserDefaults.standard.set(anchorData, forKey: "walkingActivityAnchor") } else { UserDefaults.standard.removeObject(forKey: "walkingActivityAnchor") } } } HKAnchoredObjectQuery internal func observeWalkingActivityInBackground( _ start: Bool, toRead: Set<HKQuantityType>, completion: @escaping @Sendable (Result<WalkingActivityData?, Error>) -> Void ) { if start { guard (walkingActivityQuery == nil) else { return } let predicate = getPredicate(date: Date()) let queryDescriptors = toRead.map { HKQueryDescriptor(sampleType: $0, predicate: predicate) } let handleSamples: @Sendable (HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, Error?) -> Void = { [weak self] _, samples, _, newAnchor, error in guard let self = self else { return } if let error = error { completion(.failure(error)) return } guard let samples = samples, !samples.isEmpty else { completion(.success(nil)) return } Task { self.walkingActivityQueryAnchor = newAnchor let activity = await self.getWalkingActivity(date: Date()) completion(.success(activity)) } } let query = HKAnchoredObjectQuery( queryDescriptors: queryDescriptors, anchor: walkingActivityQueryAnchor, limit: HKObjectQueryNoLimit, resultsHandler: handleSamples ) query.updateHandler = handleSamples healthStore.execute(query) walkingActivityQuery = query } else { if let query = walkingActivityQuery { healthStore.stop(query) walkingActivityQuery = nil } } } WalkingActivityData private func getWalkingActivity(date: Date) async -> WalkingActivityData { async let averageHeartRate = try await self.getAverageHeartRate(date: date) async let steps = try self.getStepCount(date: date) async let durationMinutes = try self.getTotalDurationInMinutes(date: date) async let distanceMeters = try self.getDistanceWalkingRunning(date: date, unit: .meter()) async let activeCalories = try self.getActiveEnergyBurned(date: date) return await WalkingActivityData( date: date, steps: try? steps, activeCalories: try? activeCalories, distanceMeters: try? distanceMeters, durationMinutes: try? durationMinutes, averageHeartRate: try? averageHeartRate ) } Example of getAverageHeartRate func getAverageHeartRate(date: Date) async throws -> Double? { let type = HKQuantityType(.heartRate) _ = try checkAuthorizationStatus(for: type) guard let heartRate = try await getDescriptor( date: date, type: type, options: .discreteAverage ).result(for: healthStore) .statistics(for: date)? .averageQuantity()?.doubleValue(for: HKUnit.count().unitDivided(by: HKUnit.minute())) else { return nil } return Double(String(format: "%.2f", heartRate)) ?? 0.0 } Descriptor & predicate internal func getPredicate(startDate: Date, endDate: Date) -> NSCompoundPredicate { let predicateForSamples = HKQuery.predicateForSamples(withStart: startDate, end: endDate) let excludeManual = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered) return NSCompoundPredicate(andPredicateWithSubpredicates: [predicateForSamples, excludeManual]) } internal func getDescriptor(startDate: Date, endDate: Date, type: HKQuantityType, options: HKStatisticsOptions) -> HKStatisticsCollectionQueryDescriptor { let calendar = Calendar(identifier: .gregorian) let anchorDate = calendar.date(bySetting: .hour, value: 0, of: startDate)! var interval = DateComponents() interval.day = 1 return HKStatisticsCollectionQueryDescriptor( predicate: HKSamplePredicate.quantitySample(type: type, predicate: getPredicate(startDate: startDate, endDate: endDate)), options: options, anchorDate: anchorDate, intervalComponents: interval ) } Implementation public func observeWalkingActivityInBackground(_ start: Bool, toRead: Set<HKQuantityType>, memberID: String) { observeWalkingActivityInBackground(start, toRead: toRead) { [weak self] result in guard let self = self else { return } } }
1
0
211
Mar ’25
Alternative to MAC Address for Uniqueness in iOS Bluetooth Connection
I am developing a React Native app for a health monitoring device that connects via Bluetooth and streams live data on iOS. To ensure the uniqueness of the device, I initially planned to use the MAC address. However, I discovered that iOS does not provide access to the original MAC address due to privacy restrictions. Is there an alternative approach to uniquely identifying a Bluetooth device in iOS? I need a reliable way to distinguish devices while maintaining secure and stable connections. Any insights or best practices on handling this in iOS would be greatly appreciated. Looking forward to hearing your suggestions! If anyone has experience with handling Bluetooth device uniqueness on iOS, please share your insights. Thank you!
1
0
51
Apr ’25
Synchronization Timing Between Apple Watch HealthKit Store and iPhone HealthKit Store
Hi, I’m currently working on an app that utilizes sleep data from HealthKit to provide users with meaningful insights about their sleep. To ensure a smooth user experience, I’d like to understand when sleep data collected by the Apple Watch is saved to the HealthKit store and when it gets synced to the iPhone. Ideally, I want to fetch sleep data right after the user wakes up and opens our app. However, to do this reliably, I need to know the timing of how and when this data becomes available in the iPhone’s HealthKit store. I’ve looked through the official documentation and relevant WWDC sessions but couldn’t find clear information on this topic. If anyone has insights or experience with how and when the Apple Watch syncs HealthKit data—especially sleep records—to the iPhone, I’d greatly appreciate your input. Thanks!
1
0
76
Apr ’25
Detecting Sleep End Events and Sleep Data Sync Timing from Apple Watch to HealthKit on iPhone
Hello, I’m developing an iOS app that works with sleep data from Apple Watch via HealthKit. I would like to clarify the following: How can an iPhone app detect when a sleep session ends on the Apple Watch? When is sleep data typically written to the HealthKit store on iPhone after sleep ends? Is it immediately after wake-up, or does it depend on certain conditions (e.g., watch charging, connectivity)? Understanding the timing and mechanism of sleep data synchronization is crucial for our app to process accurate and timely health information. Thank you for your assistance.
1
0
42
Apr ’25