Hello fellow developers,
I'm using Xcode 14, MacOS 13.2, and trying to make an iPhone and Apple Watch companion app using HealthKit. I'm getting the following error:
Code=4 "Missing com.apple.developer.healthkit entitlement." UserInfo={NSLocalizedDescription=Missing com.apple.developer.healthkit entitlement.}
From this line of code:
private var healthStore = HKHealthStore()
Since Xcode there is no Info.plist file and I've tried adding it but the build fails if I add a properties file that is called Info.plist.
I've seen recent documentation that states to use project -> Info -> Custom iOS Target Properties and values such as:
"Privacy - Health Update Usage Description" and add a description greater than 12 characters. However, this property and other properties that I add to this section section disappear when I navigate away from that tab.
This is weird because if I try to re-enter that value, I am presented with a dialog that states, "Replace Existing Pair". Again, I can add it and then navigate away and it disappears.
Regardless, I can't find any documentation in the Apple developer forums or any other part of the Internet. Is this a bug?
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)")
}
}
}
}
}
}