How to fetch Resting energy value from HealthKit which has same value as Health App's value? - ios

I'm using apple's HealthKit sample however resting energy value shown in Health app in iPhone doesn't match with the value fetched in sample app.
As per apple docs, HKQuantityTypeIdentifierBasalEnergyBurned is representing resting energy so I fetched this value from the HealthKit but the value I received doesn't match with the resting energy shown in Health App.
So I came across the apple's HealthKit sample where they are calculating resting energy based on formula:
// Calculates the user's total basal (resting) energy burn based off of their height, weight, age,
// and biological sex. If there is not enough information, return an error.
private func fetchTotalBasalBurn(completion: #escaping (HKQuantity?, Error?) -> Void)
{
let todayPredicate: NSPredicate = self.predicateForSamplesToday()
let weightType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.bodyMass)!
let heightType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.height)!
let queryWeigth: HKCompletionHandle = {
(weight, error) -> Void in
guard let weight = weight else {
completion(nil, error)
return
}
let queryHeigth: HKCompletionHandle = {
(height, error) -> Void in
if height == nil {
completion(nil, error)
return;
}
var dateOfBirth: Date!
do {
dateOfBirth = try self.healthStore!.dateOfBirth()
} catch {
completion(nil, error)
return
}
var biologicalSexObjet: HKBiologicalSexObject!
do {
biologicalSexObjet = try self.healthStore!.biologicalSex()
} catch {
completion(nil, error)
return
}
// Once we have pulled all of the information without errors, calculate the user's total basal energy burn
let basalEnergyButn: HKQuantity? = self.calculateBasalBurnTodayFromWeight(weight, height: height, dateOfBirth: dateOfBirth!, biologicalSex: biologicalSexObjet)
completion(basalEnergyButn, nil)
}
if let healthStore = self.healthStore {
healthStore.mostRecentQuantitySample(ofType: heightType, predicate: todayPredicate, completion: queryHeigth)
}
}
if let healthStore = self.healthStore {
healthStore.mostRecentQuantitySample(ofType: weightType, predicate: nil, completion: queryWeigth)
}
}
private func calculateBasalBurnTodayFromWeight(_ weight: HKQuantity?, height: HKQuantity?, dateOfBirth: Date, biologicalSex: HKBiologicalSexObject) -> HKQuantity?
{
// Only calculate Basal Metabolic Rate (BMR) if we have enough information about the user
guard let weight = weight, let height = height else {
return nil
}
// Note the difference between calling +unitFromString: vs creating a unit from a string with
// a given prefix. Both of these are equally valid, however one may be more convenient for a given
// use case.
let heightInCentimeters: Double = height.doubleValue(for: HKUnit(from:"cm"))
let weightInKilograms: Double = weight.doubleValue(for: HKUnit.gramUnit(with: HKMetricPrefix.kilo))
let nowDate = Date()
let ageComponents: DateComponents = Calendar.current.dateComponents([Calendar.Component.year], from: dateOfBirth, to: nowDate)
let ageInYears: Int = ageComponents.year!
// BMR is calculated in kilocalories per day.
let BMR: Double = self.calculateBMRFromWeight(weightInKilograms: weightInKilograms, height: heightInCentimeters, age: ageInYears, biologicalSex: biologicalSex.biologicalSex)
// Figure out how much of today has completed so we know how many kilocalories the user has burned.
let (startOfToday, endOfToday): (Date, Date) = self.datesFromToday()
let secondsInDay: TimeInterval = endOfToday.timeIntervalSince(startOfToday)
let percentOfDayComplete: Double = nowDate.timeIntervalSince(startOfToday) / secondsInDay
let kilocaloriesBurned: Double = BMR * percentOfDayComplete
let basalBurn = HKQuantity(unit: HKUnit.kilocalorie(), doubleValue: kilocaloriesBurned)
return basalBurn
}
/// Returns BMR value in kilocalories per day. Note that there are different ways of calculating the
/// BMR. In this example we chose an arbitrary function to calculate BMR based on weight, height, age,
/// and biological sex.
private func calculateBMRFromWeight(weightInKilograms: Double, height heightInCentimeters: Double, age ageInYears: Int, biologicalSex: HKBiologicalSex) -> Double
{
var BMR: Double = 0
if biologicalSex == .male {
BMR = 66.0 + (13.8 * weightInKilograms) + (5.0 * heightInCentimeters) - (6.8 * Double(ageInYears))
return BMR
}
BMR = 655 + (9.6 * weightInKilograms) + (1.8 * heightInCentimeters) - (4.7 * Double(ageInYears))
return BMR
}
I'm tried the sample app to fetch resting energy however still resting energy value shown in health app and sample app doesn't have same value.
Could any body tell me how to fetch resting energy or what is the calculation used by Health App to find resting energy?
It would be great if someone can give me some pointers on it, I'm pretty new to HealthKit.
Thanks.

It seems that Apple's sample is outdated. As of iOS 8 and watchOS 2 there's a call to retrieve this information in the same way that active calories are retrieved; simply change the identifier. Apple Documentation
HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.basalEnergyBurned)
Don't forget to include the additional permission to read this data as well.

Related

How to get workout heart rate details durations and calculate data like Fat burn/Build fitness

I am building an apple health kit app and now I want to generate how much time users spent on workouts and how much time spent on certain states like extreme, fat burn and some others.
Like this following image:
So far I have got workouts from sample query like this.
func readWorkouts() async -> [HKWorkout]? {
clearAllWorkoutData()
let predicate = HKQuery.predicateForSamples(withStart: startDate.getDateOnly().toDateOnly(), end: endDate, options: .strictStartDate)
let samples = try! await withCheckedThrowingContinuation { (continuation: CheckedContinuation<[HKSample], Error>) in
store.execute(HKSampleQuery(sampleType: .workoutType(), predicate: predicate, limit: HKObjectQueryNoLimit,sortDescriptors: [.init(keyPath: \HKSample.startDate, ascending: false)], resultsHandler: { query, samples, error in
if let hasError = error {
continuation.resume(throwing: hasError)
return
}
guard let samples = samples else {
fatalError("*** Invalid State: This can only fail if there was an error. ***")
}
continuation.resume(returning: samples)
}))
}
guard let workouts = samples as? [HKWorkout] else {
return nil
}
for item in workouts {
Task{
await
self.calculateOtherWorkoutData(startDate: item.startDate, endDate: item.endDate, item: item)
}
}
return workouts
}
Then I called a function to calculate those data.
func calculateOtherWorkoutData(startDate:Date,endDate:Date,item:HKWorkout) async{
let hrType = HKQuantityType.quantityType(forIdentifier: .heartRate)!
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
let samples = try! await withCheckedThrowingContinuation { (continuation: CheckedContinuation<[HKSample], Error>) in
store.execute(HKSampleQuery(sampleType: hrType, predicate: predicate, limit: HKObjectQueryNoLimit,sortDescriptors: [.init(keyPath: \HKSample.startDate, ascending: false)], resultsHandler: { query, samples, error in
if let hasError = error {
continuation.resume(throwing: hasError)
return
}
guard let samples = samples else {
fatalError("*** Invalid State: This can only fail if there was an error. ***")
}
continuation.resume(returning: samples)
}))
}
guard let heartrates = samples as? [HKQuantitySample] else {
return
}
for heartrate in heartrates {
let beats: Double? = heartrate.quantity.doubleValue(for: HKUnit.count().unitDivided(by: HKUnit.minute()))
print(heartrate.startDate, heartrate.endDate)
guard beats != nil else {
return
}
let workoutDiff = item.duration
if beats! > 0 && beats! < 93{
self.warmthTime += heartrate.endDate.timeIntervalSince(heartrate.startDate)
}
if beats! > 93 && beats! < 111{
self.fatBurnTime += heartrate.endDate.timeIntervalSince(heartrate.startDate)
}
if beats! > 111 && beats! < 130{
self.buildFitTime += heartrate.endDate.timeIntervalSince(heartrate.startDate)
}
if beats! > 130 && beats! < 148{
self.highIntTime += heartrate.endDate.timeIntervalSince(heartrate.startDate)
}
if beats! > 167{
self.extremeTime += heartrate.endDate.timeIntervalSince(heartrate.startDate)
}
}
}
But I am stuck on getting those durations. if I use total workout durations it calculates again and again. The health kit is measuring heart rates on times but not with durations.
I also tried statistical queries but it only gives those averages and only one heart rate data is coming for one workout like that.
Does anyone have an idea how to accomplish this like in the image I attached?

Get heartRateVariabilitySDNN during LiveWorkoutSession at WatchOS

I've a made a simple watchOS app to run a running session and monitor heart rate variability(sdnn). Running on a simulator I succeed to get other p-s like distance, heart rate or calories, but not sdnn.
Here is my way to setup a WorkoutManager:
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
//
}
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
for type in collectedTypes{
print(type)
guard let quantityType = type as? HKQuantityType else {return}
let statistics = workoutBuilder.statistics(for: quantityType)
updateForStatistics(statistics)
}
}
}
And this piece is fetching data in realtime:
func updateForStatistics(_ statistics: HKStatistics?) {
guard let statistics = statistics else { return }
DispatchQueue.main.async {
switch statistics.quantityType {
case HKQuantityType.quantityType(forIdentifier: .heartRate):
let heartRateUnit = HKUnit.count().unitDivided(by: HKUnit.minute())
self.heartRate = statistics.mostRecentQuantity()?.doubleValue(for: heartRateUnit) ?? 0
self.averageHeartRate = statistics.averageQuantity()?.doubleValue(for: heartRateUnit) ?? 0
case HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned):
let energyUnit = HKUnit.kilocalorie()
self.activeEnergy = statistics.sumQuantity()?.doubleValue(for: energyUnit) ?? 0
case HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning), HKQuantityType.quantityType(forIdentifier: .distanceCycling):
let meterUnit = HKUnit.meter()
self.distance = statistics.sumQuantity()?.doubleValue(for: meterUnit) ?? 0
case HKQuantityType.quantityType(forIdentifier: .heartRateVariabilitySDNN):
let sdnnUnit = HKUnit.count()
self.sdnn = statistics.mostRecentQuantity()?.doubleValue(for: sdnnUnit) ?? 0
default:
return
}
}
}
As mentioned, all other p-s are emulating by WatchOS excluding sdnn - here I always getting no data.
Also, I know how to write my own sdnn values through the HKQuantitySample, but need the specific ones tracked by OS during the session. Or some workaround to force OS to save this for me.
Any ideas, please?
The ssdUnit should be HKUnit(from: "ms")
Unfortunately, You cannot access heartRateVariabilitySDNN parameter because it is not measured during workout. However, you can manually trigger measurement of HRV by using Breathe exercise in Mindfulness app

HealthKit Blood Oxygen SPO2

With the series 6 Apple Watch, you can now get a measure of your SP02, hemoglobin content in your blood oxygen. The health app on the iPhone shows you all the measurements in the Respiratory section. This is a critical component for COVID patients.
I have not been able to find anyway to access this information programatically.
I have checked all HKObjectTypes in the latest Apple documentation. Is this information currently available to iOS developers?
Any information would be of great use as several researchers are requesting it.
Ok, I am being told that this is the same as Oxygen Saturation.Here is the code I use to query HK for Oxygen Saturation:
// Get SPO2
func getOxygenSaturation()
{
// Type is SPO2 SDNN
let osType:HKQuantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.oxygenSaturation)!
let predicate = HKQuery.predicateForSamples(withStart: Date.distantPast, end: Date(), options: .strictEndDate)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: false)
let osUnit:HKUnit = HKUnit(from: "%")
let osQuery = HKSampleQuery(sampleType: osType,
predicate: predicate,
limit: 10,
sortDescriptors: [sortDescriptor]) { (query, results, error) in
guard error == nil else { print("error"); return }
// Get the array of results from the sample query
let sampleArray:[HKSample]? = results!
// Loop through the array of rsults
for (_, sample) in sampleArray!.enumerated()
{
// Be sure something is there
if let currData:HKQuantitySample = sample as? HKQuantitySample
{
let os: Double = (currData.quantity.doubleValue(for: osUnit) * 100.0)
let d1: Date = currData.startDate
let str1 = SwiftLib.returnDateAndTimeWithTZ(date: d1, info: self.info!)
Dispatch.DispatchQueue.main.async {
self.tvOxygenValue.text = String(format: "%.0f%#", os, "%");
self.tvOxygenDate.text = str1
//print("\(os)");
}
}
}
print("Done")
self.loadAndDisplayActivityInformation()
}
healthStore!.execute(osQuery)
}

How to differentiate sources with HealthKit sleep query

I'm currently using the following code to query for the number of hours the user was asleep in the last 24 hours:
func getHealthKitSleep() {
let healthStore = HKHealthStore()
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// Get all samples from the last 24 hours
let endDate = Date()
let startDate = endDate.addingTimeInterval(-1.0 * 60.0 * 60.0 * 24.0)
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: [])
// Sleep query
let sleepQuery = HKSampleQuery(
sampleType: HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis)!,
predicate: predicate,
limit: 0,
sortDescriptors: [sortDescriptor]){ (query, results, error) -> Void in
if error != nil {return}
// Sum the sleep time
var minutesSleepAggr = 0.0
if let result = results {
for item in result {
if let sample = item as? HKCategorySample {
if sample.value == HKCategoryValueSleepAnalysis.asleep.rawValue && sample.startDate >= startDate {
let sleepTime = sample.endDate.timeIntervalSince(sample.startDate)
let minutesInAnHour = 60.0
let minutesBetweenDates = sleepTime / minutesInAnHour
minutesSleepAggr += minutesBetweenDates
}
}
}
self.sleep = Double(String(format: "%.1f", minutesSleepAggr / 60))!
print("HOURS: \(String(describing: self.sleep))")
}
}
// Execute our query
healthStore.execute(sleepQuery)
}
This works great if the user has only one sleep app as the source for the data. The problem is if the user is using 2 sleep apps, for example, as sources, the data will be doubled. How can I differentiate the sources? If able to differentiate the sources, I would like to either only grab data from one source, or maybe take the average of the sources.
When you're looping over the samples, you can access information about the source for each. I only accept a single source, so I just keep a variable of the source name and if the current sample has a different source name I continue looping without processing the data from that sample, but you could combine the data in other ways if you wanted to.
Here's how to access the source info:
if let sample = item as? HKCategorySample {
let name = sample.sourceRevision.source.name
let id = sample.sourceRevision.source.bundleIdentifier
}
There's some more info on the HKSourceRevision object in the docs here.

Firebase iOS -Cast StorageUploadTask Progress From Double to Int

I'm uploading a video file to Firebase Storage and I'm using a progress Indicator to show it's progress. Everything works except I can't get it to display Ints (whole numbers), it's displaying Doubles like:
The numbers should be whole numbers like 0%, 33%, and 96%
I tried casting the percentLabel of type Double to Int but it kept crashing:
let percentComplete: Double = 100.0 * Double(completedUnitCount) / Double(totalUnitCount)
self?.progressLabel.text = "\(Int(percentComplete))%"
How can I get these Doubles to become Ints?
var uploadTask: StorageUploadTask?
uploadTask = videoIdRef?.putFile(from: videoUrl)
uploadTask?.observe(.progress, handler: {
[weak self] (snapshot) in
if let completedUnitCount = snapshot.progress?.completedUnitCount, let totalUnitCount =
snapshot.progress?.totalUnitCount {
let uploadProgress:Float = Float(completedUnitCount) / Float(totalUnitCount)
self?.progressView.progress = uploadProgress
let percentComplete = 100.0 * Double(completedUnitCount) / Double(totalUnitCount)
self?.progressLabel.text = "\(percentComplete)%"
}
})
You could use simple String initializer for this
String(format: "%.0f", floor(percentComplete))

Resources