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!
Use HealthKit to enable your iOS and watchOS apps to work with the Apple Health app.
Post
Replies
Boosts
Views
Activity
I'm using Healthkit with the following H/W specs:
Apple Watch, series 8, OS: 10.6.1 (21U580)
iPhone 11 Pro, OS: 17.6.1
Mac Studio M1
Xcode ver: 16.0 (16A242d)
I am trying to get Apple Watch to report heart rate, HRV, respiratory rate, and body temperature using Healthkit's HKLiveWorkoutBuilder implementing HKLiveWorkoutBuilderDelegate's workoutBuilder method. However, the only reported value that is found from the workoutBuilder method's collectedTypes (a Set of HKSampleType objects) is HKQuantityTypeIdentifierHeartRate. Nothing for HRV, respiratory rate, or body temperature. All entitlements are set up, the plist filled in, and capabilities in place. Not sure why only the heart rate is reported from the watch but nothing else.
I've scoured StackOverflow, Apple developer forums, even ChatGPT but none of the solutions work.
Any help most appreciate!
The model code is:
import Foundation
import HealthKit
class WatchModel: NSObject, HKLiveWorkoutBuilderDelegate, HKWorkoutSessionDelegate {
private let healthStore = HKHealthStore()
private var workoutSession: HKWorkoutSession!
private var workoutBuilder: HKLiveWorkoutBuilder!
override init() {
super.init()
requestAuthorization()
startWorkoutSession()
}
private func requestAuthorization() {
let heartRateType = HKQuantityType.quantityType(forIdentifier: .heartRate)!
let respiratoryRateType = HKQuantityType.quantityType(forIdentifier: .respiratoryRate)!
let HRVRateType = HKQuantityType.quantityType(forIdentifier: .heartRateVariabilitySDNN)!
let temperatureRateType = HKQuantityType.quantityType(forIdentifier: .bodyTemperature)!
let healthDataTypes: Set = [heartRateType, respiratoryRateType, HRVRateType, temperatureRateType]
healthStore.requestAuthorization(toShare: healthDataTypes, read: healthDataTypes) { (success, error) in
if !success {
print("Authorization failed")
}
}
}
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
}
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: any Error) {
}
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
}
func startWorkoutSession() {
let configuration = HKWorkoutConfiguration()
configuration.activityType = .other
configuration.locationType = .indoor
do {
workoutSession = try HKWorkoutSession(healthStore: healthStore, configuration: configuration)
workoutBuilder = workoutSession.associatedWorkoutBuilder()
workoutBuilder.delegate = self
workoutBuilder.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, workoutConfiguration: configuration)
let dataSource = HKLiveWorkoutDataSource(healthStore: healthStore, workoutConfiguration: configuration)
let respiratoryRate = HKQuantityType(.respiratoryRate)
dataSource.enableCollection(for: respiratoryRate, predicate: nil)
let bodyTemp = HKQuantityType(.bodyTemperature)
dataSource.enableCollection(for: bodyTemp, predicate: nil)
let hrv = HKQuantityType(.heartRateVariabilitySDNN)
dataSource.enableCollection(for: hrv, predicate: nil)
workoutSession.delegate = self
workoutSession.startActivity(with: Date())
workoutBuilder.beginCollection(withStart: Date(), completion: { (success, error) in
if let error = error {
print("Error starting collection: \(error.localizedDescription)")
}
})
} catch {
print("Failed to start workout session: \(error.localizedDescription)")
}
}
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
print("collected types: \(collectedTypes)")
for type in collectedTypes {
if let quantityType = type as? HKQuantityType {
if quantityType == HKQuantityType.quantityType(forIdentifier: .heartRate) {
if let heartRateQuantity = workoutBuilder.statistics(for: quantityType)?.mostRecentQuantity() {
let heartRateUnit = HKUnit(from: "count/min")
let heartRateValue = heartRateQuantity.doubleValue(for: heartRateUnit)
print("heart rate: \(heartRateValue)")
}
}
if quantityType == HKQuantityType.quantityType(forIdentifier: .heartRateVariabilitySDNN) {
if let hrvQuantity = workoutBuilder.statistics(for: quantityType)?.mostRecentQuantity() {
let hrvUnit = HKUnit.secondUnit(with: .milli)
let hrvValue = hrvQuantity.doubleValue(for: hrvUnit)
print("HRV: \(hrvValue)")
}
}
if quantityType == HKQuantityType.quantityType(forIdentifier: .bodyTemperature) {
if let bodyTempQuantity = workoutBuilder.statistics(for: quantityType)?.mostRecentQuantity() {
let tempUnit = HKUnit.degreeCelsius()
let tempValue = bodyTempQuantity.doubleValue(for: tempUnit)
print("body temp: \(tempValue)")
}
}
if quantityType == HKQuantityType.quantityType(forIdentifier: .respiratoryRate) {
if let respRateQuantity = workoutBuilder.statistics(for: quantityType)?.mostRecentQuantity() {
let respRateUnit = HKUnit(from: "count/min")
let respRateValue = respRateQuantity.doubleValue(for: respRateUnit)
print("breathing: \(respRateValue)")
}
}
}
}
}
}
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
My team and I are working on an app for a private emergency helpline.
Now as far as I understand the (sparse) API documentation for fall detection, given the appropriate entitlement, the following happens upon a detected fall:
The Standard UI will be opened with the options to a) call SOS b) acknowledge the fall but state that you're fine though, c) deny the fall, and (implicitly after 60 seconds on inactivity) call SOS because you didn't react.
All fall detection apps will then receive a bit of background time and get the func fallDetectionManager(CMFallDetectionManager, didDetect: CMFallDetectionEvent, completionHandler: () -> Void) called with the appropriate event value.
Now that's all good and it sounds like the custom fall detection is additive to the standard system.
But but why is there something like that in the entitlement request form sheet:
For any emergency calling features that you do not provide, explain any mitigations you use to make sure the user receives emergency services support that’s as close as possible to what they’d receive had they placed an emergency call natively.
This sounds like our app would rather be a drop-in to the standard SOS service – in contrast to being additive and also in contrast to what the API documentation infers.
Am I misunderstanding something?
I noticed last night that workouts I have been recording on my main carry device running 17.6.x have not been syncing to my beta devices running iOS 18 RC, iPadOS 18 RC and watchOS 11 RC.
All devices are using the same Apple Account and I have iCloud enabled for Health data. The iPad running the RC has the syncing enabled in Profile.
Is anyone else experiencing health data not propagating to the 18.x devices? Some of data exists on all devices but not all. For good measure I left the device unlocked on the health app last night for a long period of time to let it do its thing. This morning the data still hadn't propagated.
I disabled and reenabled the synchronization on my iPad having chosen to delete all samples when disabling it. Hopefully all of my data dating back to the first Apple Watch in 2014 restores.
FWIW my data set according to iCloud settings my health dataset is just shy of 650 MB.
FB15102443 - Health / HealthKit: Workouts, activity rings, sample data, and more not syncing via iCloud to 18 RC device
I’m trying to use BGProcessingTaskRequest to fetch step data in the background and send it. However, when I combine BGProcessingTaskRequest, HKObserverQuery, and healthStore.enableBackgroundDelivery, the results sometimes return zero. When I don’t schedule the BGProcessingTaskRequest, the data retrieved using HKObserverQuery and HKSampleQueryDescriptor is correct.
// Register Smart Walking Sync Task
func registerSmartWalkingSync() {
#if !targetEnvironment(simulator)
BGTaskScheduler.shared.register(forTaskWithIdentifier: BGTaskIdentifier.smartwalking.rawValue, using: nil) { task in
guard let task = task as? BGProcessingTask else { return }
self.handleSmartWalkingSync(task: task)
}
#endif
}
func scheduleSmartWalkingSync(in seconds: TimeInterval? = nil, at date: Date? = nil) {
let newRequest = BGProcessingTaskRequest(identifier: BGTaskIdentifier.smartwalking.rawValue)
newRequest.requiresNetworkConnectivity = true
newRequest.requiresExternalPower = false
if let seconds = seconds {
newRequest.earliestBeginDate = Date().addingTimeInterval(seconds)
} else if let date = date {
newRequest.earliestBeginDate = date
}
do {
try BGTaskScheduler.shared.submit(newRequest)
debugPrint("✅ [BGTasksManager] scheduled for Smart Walking Sync")
} catch {
FirebaseConnection.shared.recordException(error)
debugPrint("❌ [BGTasksManager] error: \(error)")
}
}
// Handle Smart Walking Sync Task
func handleSmartWalkingSync(task: BGProcessingTask) {
debugPrint("🔄 [BGTasksManager] sync \(task.identifier) sync started")
scheduleSmartWalkingSync(in: SYNC_SMARTWALKING_TIME_INTERVAL)
let queue = OperationQueue()
let operation = HealthActivitiesOperation()
operation.completionBlock = {
Task {
do {
try await operation.sync()
task.setTaskCompleted(success: !operation.isCancelled)
debugPrint("✅ [BGTasksManager] sync \(task.identifier) completed successfully")
} catch {
FirebaseConnection.shared.recordException(error)
task.setTaskCompleted(success: false)
debugPrint("❌ [BGTasksManager] sync \(task.identifier) error: \(error)")
}
}
}
task.expirationHandler = {
operation.cancel()
}
queue.addOperation(operation)
}
// MARK: - HealthKit Background Delivery
internal func enableBackgroundDeliveryForAllTypes() async throws {
for type in allTypes.filter({ type in
type != HKQuantityType(.heartRate)
}) {
try await healthStore.enableBackgroundDelivery(for: type, frequency: .daily)
}
debugPrint("✅ [HealthKitManager] Enable Background Delivery")
}
internal func observeHealthKitQuery(predicate: NSPredicate?) async throws -> Set<HKSampleType> {
let queryDescriptors: [HKQueryDescriptor] = allTypes
.map { type in
HKQueryDescriptor(sampleType: type, predicate: predicate)
}
return try await withCheckedThrowingContinuation { continuation in
var hasResumed = false
let query = HKObserverQuery(queryDescriptors: queryDescriptors) { query, updatedSampleTypes, completionHandler, error in
if hasResumed {
return
}
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: updatedSampleTypes ?? [])
}
hasResumed = true
completionHandler()
}
healthStore.execute(query)
}
}
internal func getHealthActivity(by date: Date, predicate: NSCompoundPredicate, sampleTypes: Set<HKSampleType>) async throws -> HealthActivityData {
var data = HealthActivityData(steps: 0, calories: 0, distance: 0.0, distanceCycling: 0.0, totalDuration: 0, date: date, heartRate: nil)
for sampleType in sampleTypes {
guard let quantityType = sampleType as? HKQuantityType else {
continue
}
switch quantityType {
case HKQuantityType(.stepCount):
let stepCount = try await getDescriptor(
date: date,
type: quantityType
).result(for: healthStore)
.statistics(for: date)?.sumQuantity()?.doubleValue(for: HKUnit.count())
data.steps = stepCount ?? 0.0
// Calculate total duration using HKSampleQueryDescriptor
let totalDurationDescriptor = HKSampleQueryDescriptor(
predicates: [.quantitySample(type: quantityType, predicate: predicate)],
sortDescriptors: [SortDescriptor(\.endDate, order: .reverse)]
)
let stepSamples = try await totalDurationDescriptor.result(for: healthStore)
data.totalDuration += stepSamples
.reduce(0) { $0 + $1.endDate.timeIntervalSince($1.startDate) } / 60.0
default:
debugPrint("Unknown quantity type")
}
}
return data
}
Heya,
I'm currently building out my own application for tracking my health information, and I'm hoping to collect my historical data from Apple Health.
Sadly, it would appear that certain things I wish to export don't appear in the export.xml file.
Some of the things that I would expect to find in the export.xml file, that do not currently appear, are as follows:
More data about my medications, currently I can only export a list of my medications, its not possible to export data such as when what medication was taken.
Logged emotions; there is currently no support for this (that I can find).
Would appreciate some insight into this.
Hi there!
I've using the betas for a month now, and 18.1 at the moment.
I've always had my Health App crashing once I tap on "Treatments".
Is it a know bug? Is it working properly on your phones?
I've already sent a feedback, but got not answer yet.
Thanks ^^
must add the same happens on my iPad using beta version as well
Hi! I've added the code of the multidevice workout app from Apple to my app and I found some issues that I cannot see on the sample app.
In my app, to pause or end the workout from the watch, the iOS companion app has to be opened or at least on background, but never closed.
What I'm doing wrong?
Hi All,
I am posting this to get some help on fetching Water intake data from Health Kit.
I have done (following a similar approach, and perfectly working) the fetch of the user weight, but for the water, somehow, I always receive back 0 samples.
I went to the Health App, added few entries (in different days) for the water intake, under the food sections.
Then, I use the following snippet to read the values, but I always have 0 values back.
What I am doing wrong?
let type = HKQuantityType(.dietaryWater)
let samplePredicate = HKSamplePredicate.sample(type: type, predicate: nil)
// Create the descriptor.
let descriptor = HKSampleQueryDescriptor(
predicates: [samplePredicate],
sortDescriptors: [SortDescriptor(\.endDate, order: .reverse)])
let results = try await descriptor.result(for: hkHealthStore)
The results variable is always 0 elements.
What I am doing wrong?
PS: The permissions are checked and correctly given for this: HKQuantityType(.dietaryWater)
My iOS app has a correctly configured HealthKit integration. It successfully delivers all samples and data to HealthKit. For every workout sent to HealthKit, I can see the duration, workout name, calories, and other details.
However, the app icon is missing. My project uses the Single Size setting for the app icon in the XCAssets folder. The app is available in the App Store in some regions. What can I do to fix this issue?
The problem persists regardless of whether the device is running iOS 17 or 18.
On watchOS 11 when starting a workout session a widget appears automatically on the smart stack showing the pause or resume button.
It´s a great feature, but my problem is that the duration is not showed correctly because the prepare phase of the workout and the pause / resume events are not taken into account calculating the duration.
In my project I don´t use the workout builder. I have made a sample project with workout builder and there the duration is shown correctly.
It would be great if this automatically appearing widget would also show the time correctly in case the workout builder is not used (prepare phase and pause resume events considered, otherwise it looks like a bug).
Is there any way to opt out of this automatically appearing widget or could this be fixed? Any comments from an Apple engineer on this?
Hello!
My sleep process isn't recorded with iPhone only (without wearable device) on iOS 18. On previous iOS versions it was recorded but now it doesn't work.
I compare iOS 17 and iOS 18 Beta and I see the difference that in Full Schedule was deleted tumbler "Track Time in Bed with iPhone" that helped to track sleep process without wearable device (I found it here: Health app -> Browse -> Sleep -> Your schedule block and tap on "Full Schedule & Options" -> Scroll to the bottom).
I didn't find any information from Apple about this changes in change log.
And one more time: I don't use Apple Watch or any wearable device.
Do you have any information about sleep tracking with iPhone only? Can I track sleep process only with iPhone?
Are there any limitations on how long iOS will continue delivering background updates from HealthKit to the app? Will deliveries be suspended if the user stops opening the app for a few days, a month, or longer?
Hello, I am trying to build a features where I want to monitor a user's biking workout session metrics like the current speed, average speed and distance travelled. I want to get these metrics as the user is actively doing a workout session in the Workouts app of the apple watch. Is it possible? Or it is not possible to get data from an active workout session from the workouts app.
Hey folks, a couple of questions regarding WorkoutKit:
What happened to validation errors? They are mentioned in the WWDC23 video, but no longer are present in the API. It seems a fatal error is thrown if the workout fails validation (for example setting a negative distance in a workout goal).
What is the logic of the WorkoutScheduler, if we try to add more workouts than is allowed? If the limit is 50, what happens when we try to add a 51st? Are workouts de-queued based on date or anything? API documentation is very sparse
Do completed workouts and workouts with a scheduled date in the past stay in the WorkoutScheduler? Are we responsible for removing these? I guess my overall question regarding the WorkoutScheduler is, what are we responsible for managing and what is handled gracefully for us.
None of these answers seem to be openly available, but if anyone know anecdotally or even better from the WorkoutKit team, I'd be very grateful!
Thanks!
We are integrating Apple Health step data into our mobile application, Diyetkolik. However, we have received feedback from our users indicating that the step data appears to be significantly higher than expected. After conducting thorough research, we discovered that users who wear a smartwatch are experiencing step data duplication. Both the smartwatch and the phone's step data are being collected and combined, leading to inflated step counts in our application.
We kindly request your assistance in addressing the following issues:
How can we differentiate between step data from the smartwatch and the phone to avoid duplication?
Are there best practices or specific APIs we should use to filter and manage step data more accurately?
Can you provide guidance on how to implement a solution that ensures accurate step count data for users wearing multiple devices?
We appreciate your support in resolving this matter to enhance the accuracy of our application and improve user experience.
During WatchOS 10 dev betas and now into 11 dev betas, I am still seeing were stands are not being detected. What I do not know is if the steps are detected. But here is the situation.
Today:
I was going on some wiring outside and in the basement.
I was clearly up and moving around for over an hour.
I can see on my camera's I went outside at 15:50, came back in at 16:18. Then went to my basement and was working down there from 16:19 until 16:30. After that cleaned up my mess. So I was up and down stairs and without a doubt standing. At 16:50 I got the reminder to stand.
Other cases:
1: I get home from work about XX:10 . I have to walk about 50 ft from my garage to my house, up stairs, take the dog out, make dinner, and finally get my chair to eat and at XX:50 I get the reminder.
2: I'm at work, I'm going to get lunch and I have to walk pretty far and again at XX:50 I get the reminder. This is very rare, but it has happened.
Now, in a odd twist. I get up in the morning at XX:50, I have 10 steps to the bathroom, and I manage to get that hourly stand.
Hello,
The new mental health features in iOS 17 allow users to log their momentary emotions and daily moods, see valuable insights, and easily access assessments and resources. Does anyone know if HealthKit in iOS 17 provides an API for accessing these records? Thanks :)
I have installed IOS18 and WatchOS11 Beta 3 already... all complications sent to clock (usign FACER app) is not showing up... they all appears DISABLED.
doesnt matter what face watch i use.., its all disabled.
Any help on it please????