I have an App in objective-c that is using Health data (walk/run, cycling) to give advice to users . I do not want/need to write any data in the Healtkit.
If i do (with the 3 values in the plist / .info :
self.healthStore requestAuthorizationToShareTypes:nil readTypes:readDataTypes
My request crashes.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Must request authorization for at least one data type'
*** First throw call stack:
(
0 CoreFoundation 0x00000001804b910c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x0000000180092da8 objc_exception_throw + 72
2 CoreFoundation 0x00000001804b901c -[NSException initWithCoder:] + 0
3 HealthKit 0x000000019da034d4 -[HKHealthStore _validateAuthorizationRequestWithShareTypes:readTypes:] + 92
4 HealthKit 0x000000019da03670 -[HKHealthStore requestAuthorizationToShareTypes:readTypes:shouldPrompt:completion:] + 292
BUT in swift :
healthStore.requestAuthorization(toShare: nil, read: readTypes)
is working, présents only my 2 datas to read... in the same IOS , same phone without crashing. What is the difference ?
Nil object in objective-c and Nil object in swift are not the same ? how do i make readonly requests in objective C ?
HealthKit
RSS for tagAccess and share health and fitness data while maintaining the user’s privacy and control using HealthKit.
Posts under HealthKit tag
89 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
This is an ongoing issue that I haven't been able to solve:
I am querying different types of HealthKit data over the past year. While this works fine for HRV, it hangs for some users when I'm trying to get heart rate data.
Here's the relevant query
func initialRead(from startDate: Date) async throws -> [HKSample] {
let endDate = anchorStart
let interval: TimeInterval = .days(7)
var currentStartDate = startDate
var currentEndDate = Date(timeInterval: interval, since: currentStartDate)
var samples: [HKSample] = []
while currentStartDate <= endDate {
let datePredicate = SampleType.datePredicate(start: currentStartDate, end: currentEndDate)
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [datePredicate,HKQuery.predicateForObjects(withMetadataKey: HKMetadataKeyHeartRateMotionContext, allowedValues: [HKHeartRateMotionContext.sedentary])])
do {
let result = try await withCheckedThrowingContinuation { continuation in
let completionQuery = HKSampleQuery(sampleType: HKQuantityType.heartRate, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [.init(key: HKSampleSortIdentifierStartDate, ascending: true)]) { query, samples, error in
if let samples {
continuation.resume(returning: samples)
} else {
if let error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: [])
}
}
}
healthStore.execute(completionQuery)
}
samples = samples.merge(from: result)
} catch {
Logger.general.error("Reading failed for dates \(currentStartDate) to \(currentEndDate): \(error)")
}
currentStartDate = currentEndDate
currentEndDate = Date(timeInterval: interval, since: currentStartDate)
}
return samples
}
extension HKSampleType {
static func datePredicate( start:Date?, end:Date?) -> NSPredicate {
HKQuery.predicateForSamples(withStart: start, end:end, options: .strictStartDate)
}
}
For reference, I expect about 1000 sedentary samples per week. Basically what happens for these users is when they start reading the HR data, the app hangs. They start each read manually via a special TestFlight build with buttons for starting the different data type readings.
Any advice on how to proceed with this bug would be great since it only affects some users.
I am able to create test builds for this audience to test different options. One theory is the motion context predicate is screwing something up. If any apple dev can enlighten me how to narrow down the issue, that would be great.
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 }
}
}
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)
}
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!
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!
Topic:
App & System Services
SubTopic:
Health & Fitness
Tags:
Health and Fitness
HealthKit
WorkoutKit
The background deliver works perfectly when the app is in the background or suspended states. However, when the app is killed (terminated state), the background task does not execute
Apple Watch automatically tracks sleep data and syncs it to the iPhone, making it available through HealthKit for historical analysis. However, there is no way to retrieve real-time data on whether a user has entered sleep, or whether they are in a specific sleep stage at any given moment.
Is it possible to provide an interface for real-time sleep status monitoring
Dear Apple Developer Support,
I am writing to request assistance with an ongoing issue I'm encountering while developing an iOS application that utilizes HealthKit to fetch heart rate data.
My goal is to display near real-time heart rate updates continuously same as displaying in the Apple Watch , I want to show in the iPhone Mobile Application for fitness related. I have implemented the following approaches:
HKSampleQuery with a Timer: I've set up a timer to periodically fetch the latest heart rate data.
Despite these efforts, I'm consistently facing the following problems:
Delayed Updates: The heart rate data displayed in the app often doesn't reflect the current heart rate being measured by the Apple Watch. There seems to be a significant synchronization delay.
Inconsistent Background Updates: Background updates, even with background app refresh enabled, are not reliable. The app often only updates when brought to the foreground or after being killed and relaunched.
Entitlements: The com.apple.developer.healthkit.background-delivery entitlement error is missing.
I have thoroughly reviewed Apple's HealthKit documentation, implemented best practices for HealthKit integration, and verified that all necessary permissions are properly configured.
I understand that HealthKit may not be designed for true real-time data, but the current level of delay and inconsistency is making it difficult to provide a useful user experience.
Could you please provide guidance on whether achieving near real-time heart rate updates continuously in an iOS app using HealthKit is possible? If so, what are the recommended strategies and best practices to overcome these limitations?
I have also tested the application on physical devices with Apple Watch, enabled background app refresh, granted permissions, and referred to HealthKit documentation.
I would appreciate any insights or suggestions you can offer to help resolve this issue.
Thank you for your time and assistance.
Sincerely,
Venu Madhav