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

Integrating Apple Fitness+ Workouts – How to Retrieve Metadata?
Platform & Version: iOS Version: 18.3.1 Development Environment: Xcode 16.2, macOS 14.6.1 Description of the Issue: We're exploring ways to better integrate Apple Fitness+ workouts into our app. We've noticed that some third-party apps, such as Strava and HealthFit, now display Fitness+ workout details, including the title, trainer, and an image. I’ve been investigating how this is possible, and the only relevant change I’ve found is that HKMetadataKeyAppleFitnessPlusCatalogIdentifier is now being set for Fitness+ workouts. However, I can’t find any public API or official documentation that explains how to use these identifiers to retrieve the associated workout details. Question: Is there an official API available to fetch metadata for Fitness+ workouts using these identifiers? Or are these third-party apps potentially accessing private APIs? If no API exists, is the only option to create a manual mapping of these identifiers—something that seems impractical given the constantly evolving Fitness+ workout catalog? Any guidance on this would be greatly appreciated. Thanks!
4
0
632
Feb ’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
695
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
265
Mar ’25
Recording a HeartbeatSeries
I need to be able to create and store a HeartbeatSeries for a given time-period from an Apple Watch, to then retrieve that data from HealthKit to be processed. I have working code which allows me to begin a workout session, which is being used to determine how long a session has been running for. I also have working code for retrieving HeartbeatSeries data from HealthKit. The issue is that no HeartbeatSeries data is being stored into HealthKit as a result of the workout session running. Whether that session is running for as little as 30 seconds or as long as 20 minutes, nothing is stored. However, when I use the the Apple "Meditation" app (formerly known as "Breathe"), I can query HealthKit afterwards and retrieve a list of individual heartbeat timings during that 2 minute period. Therefore, it IS possible to store a HeartbeatSeries from within an app on the Apple Watch. What I would like to know is, how can I use the pulse sensor built-in to the Apple Watch to be able to record a HeartbeatSeries similar to how the Meditation app does it.
3
0
117
Mar ’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
100
Apr ’25
Incorrect Step Count from Apple HealthKit Data
Hi, i'm trying to get the number of step counts a person has taken. I decided to pull the data from health kit and the number of steps are incorrect. Come to find out apple health recommends an app called pedometer++ for the number of steps counted and after testing I realized that they are getting the correct number of steps a person is taking. How can I pull the correct number of steps a person has taken? I want to be able to merge the data from watch and phone to make sure we are getting the correct number of steps but not double counting the steps either. any guidance on this would be appreciated! Here's the code snippet that i'm using right now: permissions: { read: [AppleHealthKit.Constants.Permissions.StepCount], write: [], }, }; AppleHealthKit.initHealthKit(permissions, error => { if (error) { console.log('Error initializing HealthKit: ', error); return; } else { dispatch(setAllowHealthKit(true)); getHealthKitData(); console.log('HealthKit initialized successfully'); } }); const getHealthKitData = async () => { try { const today = new Date(); const options = { startDate: new Date(today.setHours(0, 0, 0, 0)).toISOString(), endDate: new Date().toISOString(), }; const steps = await new Promise((resolve, reject) => { AppleHealthKit.getStepCount(options, (error, results) => { if (error) reject(error); resolve(results?.value); }); }); setStepsCount(steps); } catch (error) { console.error('Error fetching HealthKit data:', error); } };
6
0
197
Jul ’25
Fitness app not now show saved routes
When I set the distanceFilter = 5 (5 meters) in the GPS CLLocationManager I can't display the workout routes in the Apple Fitness app after writing the recorded GPS data to HealthKit via HKWorkoutRouteBuilder. The smaller distanceFilter, Fitness will displays the route. Should I consider setting up a small distanceFilter when developing a workout app on watchOS?
5
0
140
Jul ’25
Apple Watch Data to Server
I was wondering which is the preferred way to send a lot of data from sensors of the apple watch to server. It is preferred to send small chucks to iphone and then to server or directly send bulk data to server from watch. How does it affect battery and resources from watch ? Are there any triggers that I can use to ensure best data stream. I need to send at least once a day. Can I do it in background or do I need the user to have my app in the foreground ? Thank you in advance
1
0
165
Jun ’25
Workout Buddy not available
Has anyone seen the workout buddy options on watch OS yet? I am not able to get it on my watch. My setup is an iPhone 16 and Watch Ultra 1 with the 26 OS I am currently using beta 3. English US language on both and US as region. I am located in Germany though. I restarted both devices multiple times without any changes. Hopefully someone can help.
1
0
98
Jul ’25
Guidance / Documentation on iOS 18.6.1 Blood Oxygen Saturation
Are there any HealthKit related changes to be aware of in the new update that enables SPO2 / Blood Oxygen Saturation measurements on certain Apple Watch models within the US? I’m aware of processing happening on the phone…. But beyond that: Does this mean values are then saved to Apple Health? Do these models still take background SPO2 measurements in the same way as other models do? Are these values then visible in third party iOS apps as normal through HealthKit? Do these values sync back to the paired Apple Watch HealthKit store for third party apps to access on the Watch? For reference I have an iOS and WatchOS app that, amongst other features, provides the ability to see your SPO2 values in the Watch app, complications and in the iOS app.
7
0
259
Sep ’25
UIScene.ConnectionOptions.shouldHandleActiveWorkoutRecovery Missing?
According to the WWDC25 Presentation Track workouts with HealthKit on iOS and iPadOS, there is supposed to be a new property for restoring an active workout after a crash on iOS/iPadOS. The developer documentation also supports this. However, this property does not seem to exist in the latest Xcode 26 beta, even in projects targeting iOS 26.0 as the minimum version. Am I missing something? Has this property not been made available yet? It is actually looking like all of the new iOS 26.0 properties are missing UIScene.ConnectionOptions on my system.
3
0
182
Aug ’25
Best Practices for Continuous Background Biometric Monitoring on Apple Watch
Hello, everyone! I'm seeking some guidance on the App Store review process and technical best practices for a watchOS app. My goal is to create an app that uses HealthKit to continuously monitor a user's heart rate in the background for sessions lasting between 30 minutes and 3 hours. This app would not be a fitness or workout tracker. My primary question is about the best way to achieve this reliably while staying within the App Store Review Guidelines. Is it advisable to use the WorkoutKit framework to start a custom, non-fitness "session" for the purpose of continuous background monitoring? Are there any other recommended APIs or frameworks for this kind of background data collection on watchOS that I should be aware of? What are the key review considerations I should be mindful of, particularly regarding Guideline 4.1 (Design) and the intended use of APIs? My app's core functionality would require this kind of data for a beneficial purpose. I want to ensure my approach is technically sound and has the best chance of a successful review. Any insights or advice from developers who have experience with similar use cases would be incredibly helpful! Thank you!
1
0
340
Aug ’25
HKAnchoredObjectQuery updateHandler stops working with error
Hi! I am using the HKAnchoredObjectQuery to first get a snapshot of the initial results, and then trigger an updateHandler. I need to handle the initial results and the updates separately, which is why I implemented two completions. When I test the code, it works for a while. New and deleted samples trigger the updateHandler. However, after a while there appears an error: [connection] nw_read_request_report [C2] Receive failed with error "Software caused connection abort" Followingly, the updateHandler will stop getting triggered when I add updates in Apple health. Anyone have experience with this? func getMostRecentSample(for sampleType: HKSampleType, anchorKey: String, completion: @escaping (HKQuantitySample?, Error?) -> Swift.Void, updateHandler: @escaping (HKQuantitySample, Error?) -> Swift.Void) {     // If it is the first initialization, anchor is passed as nil     var anchor: HKQueryAnchor? = nil // Check for previous saved anchor in userdefaults     if UserDefaults.standard.object(forKey: anchorKey) != nil {       let data = UserDefaults.standard.object(forKey: anchorKey) as! Data       do {         guard let newAnchor = try NSKeyedUnarchiver.unarchivedObject(ofClass: HKQueryAnchor.self, from: data) else {           print("Could not parse anchor to HKQueryAnchor type")           return         }         anchor = newAnchor       } catch {         print("Error retreiving anchor from UserDefaults")       }     }     let query = HKAnchoredObjectQuery(type: sampleType,                      predicate: nil,                      anchor: anchor,                      limit: HKObjectQueryNoLimit                       ) { (query, samplesOrNil, _, newAnchor, errorOrNil) in       guard let samples = samplesOrNil as? [HKQuantitySample] else {         fatalError("*** An error occurred during the initial query: \(errorOrNil!.localizedDescription) ***")       }       if let anchor = newAnchor {         do {           let data = try NSKeyedArchiver.archivedData(withRootObject: anchor as Any, requiringSecureCoding: false)           UserDefaults.standard.set(data, forKey: anchorKey)         } catch {           print("Error retreiving anchor from UserDefaults")         }       }       completion(samples.last, nil)     }           // Setting up long-running query     query.updateHandler = { (query, samplesOrNil, _, newAnchor, errorOrNil) in       guard let samples = samplesOrNil as? [HKQuantitySample] else {         fatalError("*** An error occurred during an update: \(errorOrNil!.localizedDescription) ***")       }       if let anchor = newAnchor {         do {           let data = try NSKeyedArchiver.archivedData(withRootObject: anchor as Any, requiringSecureCoding: false)           UserDefaults.standard.set(data, forKey: anchorKey)         } catch {           print("Error retreiving anchor from UserDefaults")         }       }       if let sample = samples.last {         updateHandler(sample, nil)       }     }     self.healthStore.execute(query)   }
2
0
1.2k
Mar ’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?
2
0
606
1w
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
927
Dec ’24
Is Using EAS Update (OTA) Compliant with Apple App Store Policies for a Health App?
Hi everyone, I’m developing a health-related mobile app and considering using EAS Update to deliver over-the-air (OTA) updates for JavaScript code and assets. Before implementing this, I want to ensure that this approach complies with Apple App Store policies, especially given the sensitivity of health-related apps. Here are my concerns: Does using EAS Update (OTA) align with Apple’s guidelines regarding app updates and dynamic behavior changes? Are there specific rules or restrictions for health apps using OTA updates that I should be cautious of? Could this approach be flagged as violating Apple’s policies on app integrity, especially those requiring updates to go through the App Store review process? I’d greatly appreciate any insights, advice, or references to Apple’s official documentation regarding OTA updates for apps distributed through the App Store. Thanks in advance for your help!
0
0
476
Dec ’24
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
896
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
481
Jan ’25