How to query Healthkit for average heart rate with Swift - swift

I need to query HKStatistics for average heart rate, with Swift 2.2. I've learned from research the parameter I need is HKStatisticsOptionDiscreteAverage
I have code for the workout session. How can I add the function to this code below measuring heart-rate to return the heart rate average of the workout session with HKStatisticsOptionDiscreteAverage?
func createHeartRateStreamingQuery(workoutStartDate: NSDate) -> HKQuery? {
// adding predicate will not work
// let predicate = HKQuery.predicateForSamplesWithStartDate(workoutStartDate, endDate: nil, options: HKQueryOptions.None)
guard let quantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate) else { return nil }
let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: nil, anchor: anchor, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
guard let newAnchor = newAnchor else {return}
self.anchor = newAnchor
self.updateHeartRate(sampleObjects)
}
heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in
self.anchor = newAnchor!
self.updateHeartRate(samples)
}
return heartRateQuery
}
func updateHeartRate(samples: [HKSample]?) {
guard let heartRateSamples = samples as? [HKQuantitySample] else {return}
dispatch_async(dispatch_get_main_queue()) {
guard let sample = heartRateSamples.first else{return}
let value = sample.quantity.doubleValueForUnit(self.heartRateUnit)
self.label.setText(String(UInt16(value)))
// retrieve source from sample
let name = sample.sourceRevision.source.name
self.updateDeviceName(name)
self.animateHeart()
}
}

func getAVGHeartRate() {
var typeHeart = HKQuantityType.quantityType(forIdentifier: .heartRate)
var startDate = Date() - 7 * 24 * 60 * 60 // start date is a week
var predicate: NSPredicate? = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: HKQueryOptions.strictEndDate)
var squery = HKStatisticsQuery(quantityType: typeHeart!, quantitySamplePredicate: predicate, options: .discreteAverage, completionHandler: {(query: HKStatisticsQuery,result: HKStatistics?, error: Error?) -> Void in
DispatchQueue.main.async(execute: {() -> Void in
var quantity: HKQuantity? = result?.averageQuantity()
var beats: Double? = quantity?.doubleValue(for: HKUnit.count().unitDivided(by: HKUnit.minute()))
print("got: \(String(format: "%.f", beats!))")
})
})
healthStore.execute(squery)
}
This is the Swift 3 version :)

this is objective-c example of getting the average heart BPM:
HKQuantityType *typeHeart =[HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierHeartRate];
NSPredicate *predicate = [HKQuery predicateForSamplesWithStartDate:startDate endDate:endDate options:HKQueryOptionStrictStartDate];
HKStatisticsQuery *squery = [[HKStatisticsQuery alloc] initWithQuantityType:typeHeart quantitySamplePredicate:predicate options:HKStatisticsOptionDiscreteAverage completionHandler:^(HKStatisticsQuery *query, HKStatistics *result, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
HKQuantity *quantity = result.averageQuantity;
double beats = [quantity doubleValueForUnit:[[HKUnit countUnit] unitDividedByUnit:[HKUnit minuteUnit]]];
NSLog(#"got: %#", [NSString stringWithFormat:#"%.f",beats]) ;
}
);
}];
[self.healthStore executeQuery:squery];

Related

Fetched data from private CloudKit database not returning from function

I'm trying to fetch some data from a private ClouKit database. The query and predicate is working fine because I can see at print(data) // 1 that the array is filled with all the data. But as soon as it leaves perform(){} the array is resetted to it's at the top defined values and it just returns them.
func fetchUserRecord() -> [Double] {
var data: [Double] = [0, 1, 2] //demo data
let aWeekAgo = Date().addingTimeInterval(-604800)
let privateDB = CKContainer.default().privateCloudDatabase
let predicate = NSPredicate(format: "Date > %#", aWeekAgo as NSDate)
let query = CKQuery(recordType: "ProgressionValue", predicate: predicate)
privateDB.perform(query, inZoneWith: nil) { records, error in
guard let records = records else { return }
for record in records {
data.append(record["Value"] as! Double)
}
data.append(0.0)
print(data) // 1
}
print(data) // 2
return data
}
Here are the outputs:
print(data) //1:
[0.0, 1.0, 2.0, {tons of doubles}, 0.0]
print(data) //2:
[0.0, 1.0, 2.0]
What am I missing?
Try the new synch tools;
func fetchUserRecord() async throws -> [Double]
{
var data: [Double] = [0, 1, 2] // you don't really want the
let aWeekAgo = Date().addingTimeInterval(-604800)
let privateDB = CKContainer.default().privateCloudDatabase
let predicate = NSPredicate(format: "Date > %#", aWeekAgo as NSDate)
let query = CKQuery(recordType: "ProgressionValue", predicate: predicate)
let (values, cursor) = try await privateDB.records(matching: query, resultsLimit: 100)
for r in values
{
if let rec = try? r.1.get()
{
data.append(rec["value"] as Double)
}
}
return x
}
Call it like this;
Task
{
do {
data = try await fetchUserRecord()
}
catch
{
print(error)
}
}

in swift code, can we use a function without bracket?

I am now analyzing swift source code.
In this source code, performQuery is defined as function, but in line 2, performQuery is used without bracket"()". What does it mean?
Can we use a function without bracket?
I have read all the swift grammar but I cannot find similar code.
Is it related to "Unstructured Concurrency"?
==================================================
func calculateDailyQuantitySamplesForPastWeek() {
performQuery {
DispatchQueue.main.async { [weak self] in
self?.reloadData()
}
}
}
// MARK: - HealthQueryDataSource
func performQuery(completion: #escaping () -> Void) {
let predicate = createLastWeekPredicate()
let anchorDate = createAnchorDate()
let dailyInterval = DateComponents(day: 1)
let statisticsOptions = getStatisticsOptions(for: dataTypeIdentifier)
let query = HKStatisticsCollectionQuery(quantityType: quantityType,
quantitySamplePredicate: predicate,
options: statisticsOptions,
anchorDate: anchorDate,
intervalComponents: dailyInterval)
// The handler block for the HKStatisticsCollection object.
let updateInterfaceWithStatistics: (HKStatisticsCollection) -> Void = { statisticsCollection in
self.dataValues = []
let now = Date()
let startDate = getLastWeekStartDate()
let endDate = now
statisticsCollection.enumerateStatistics(from: startDate, to: endDate) { [weak self] (statistics, stop) in
var dataValue = HealthDataTypeValue(startDate: statistics.startDate,
endDate: statistics.endDate,
value: 0)
if let quantity = getStatisticsQuantity(for: statistics, with: statisticsOptions),
let identifier = self?.dataTypeIdentifier,
let unit = preferredUnit(for: identifier) {
dataValue.value = quantity.doubleValue(for: unit)
}
self?.dataValues.append(dataValue)
}
completion()
}
query.initialResultsHandler = { query, statisticsCollection, error in
if let statisticsCollection = statisticsCollection {
updateInterfaceWithStatistics(statisticsCollection)
}
}
query.statisticsUpdateHandler = { [weak self] query, statistics, statisticsCollection, error in
// Ensure we only update the interface if the visible data type is updated
if let statisticsCollection = statisticsCollection, query.objectType?.identifier == self?.dataTypeIdentifier {
updateInterfaceWithStatistics(statisticsCollection)
}
}
self.healthStore.execute(query)
self.query = query
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let query = query {
self.healthStore.stop(query)
}
}
}
you need to learn closure. please check the link

Step Count always "0" using healthKit in swift

I am trying to get last 7 days step count but it is always coming zero. But when I open the health app in iPhone then it is more than 3000 steps. Even I also added property Privacy - Health Share Usage Description and Privacy - Health Update Usage Description in .plist file.
Here is my code
var healthScore = HKHealthStore()
override func viewDidLoad() {
super.viewDidLoad()
// Access Step Count
let healthKitTypes: Set = [ HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)! ]
// Check for Authorization
healthScore.requestAuthorization(toShare: healthKitTypes, read: healthKitTypes) { (bool, error) in
if (bool) {
// Authorization Successful
self.getSteps { (result) in
DispatchQueue.main.async {
let stepCount = String(Int(result))
self.stepLbl.text = String(stepCount)
}
}
}
}
}
func getSteps(completion: #escaping (Double) -> Void){
let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let exactlySevenDaysAgo = Calendar.current.date(byAdding: DateComponents(day: -7), to: now)!
let predicate = HKQuery.predicateForSamples(withStart: exactlySevenDaysAgo, end: now, options: .strictStartDate)
let query = HKStatisticsQuery(quantityType: stepsQuantityType, quantitySamplePredicate: predicate, options: .cumulativeSum) { (_, result, error) in
var resultCount = 0.0
guard let result = result else {
print("\(String(describing: error?.localizedDescription)) ")
completion(resultCount)
return
}
if let sum = result.sumQuantity() {
resultCount = sum.doubleValue(for: HKUnit.count())
}
DispatchQueue.main.async {
completion(resultCount)
}
}
healthScore.execute(query)
}
In console I checked the dates are also correct now here's the screenshot

Dispatch Group not notifying? [duplicate]

This question already has answers here:
How to notify a queue in Swift (GCD)
(2 answers)
Closed 4 years ago.
I have this identical code in another app and it works without fail, now in this app the dispatch group is not notifying and my handler isn't getting called? I can't figure out the difference between the 2 apps since the code is identical?
func getHeartRateMaxFromWorkouts(workouts: [HKWorkout], handler: #escaping (careerMaxHeartRatePerWorkoutAsCustomHistoricalSample, careerAverageHeartRatePerWorkoutAsCustomHistoricalSample) -> Void) {
let workoutsReversed = workouts.reversed()
guard let heartRateType:HKQuantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else { return }
let heartRateUnit:HKUnit = HKUnit(from: "count/min")
var heartRateMaxArrayAsCustomHistoricalSample = [CustomHistoricalSample]()
var heartRateAvgArrayAsCustomHistoricalSample = [CustomHistoricalSample]()
//DispatchGroup needed since making async call per workout and need notified when all calls are done
let dispatchGroup = DispatchGroup()
for workout in workoutsReversed {
//predicate
let startDate = workout.startDate
let endDate = workout.endDate
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
//descriptor
let sortDescriptors = [
NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: true) //Changed this to false so that HRR and MCS would calculate, always check this if not getting these values
]
dispatchGroup.enter()
let heartRateQuery = HKSampleQuery(sampleType: heartRateType,
predicate: predicate,
limit: (HKObjectQueryNoLimit),
sortDescriptors: sortDescriptors)
{ (query:HKSampleQuery, results:[HKSample]?, error:Error?) -> Void in
guard error == nil else { print("get heart rate error"); return }
guard let unwrappedResults = results as? [HKQuantitySample] else { print("get heart rate error"); return}
let heartRatesAsDouble = unwrappedResults.map {$0.quantity.doubleValue(for: heartRateUnit)}
guard let max = heartRatesAsDouble.max() else { return }
let maxAsCustomHistoricalSample = CustomHistoricalSample(value: max, date: workout.startDate)
heartRateMaxArrayAsCustomHistoricalSample.append(maxAsCustomHistoricalSample)
let average = heartRatesAsDouble.average
let averageAsCustomHistoricalSample = CustomHistoricalSample(value: average, date: workout.startDate)
heartRateAvgArrayAsCustomHistoricalSample.append(averageAsCustomHistoricalSample)
dispatchGroup.leave()
}
healthStore.execute(heartRateQuery)
} //End of for workout loop
dispatchGroup.notify(queue: .main) {
//Need to sort by date since the dates come back jumbled
let sortedReversedHeartRateMaxArrayAsCustomHistoricalSampple = heartRateMaxArrayAsCustomHistoricalSample.sorted { $0.date > $1.date }.reversed() as [CustomHistoricalSample]
let sortedReversedHeartRateAverageArrayAsCustomHistoricalSampple = heartRateAvgArrayAsCustomHistoricalSample.sorted { $0.date > $1.date }.reversed() as [CustomHistoricalSample]
print("handler called = \(sortedReversedHeartRateMaxArrayAsCustomHistoricalSampple.count)")
handler(sortedReversedHeartRateMaxArrayAsCustomHistoricalSampple, sortedReversedHeartRateAverageArrayAsCustomHistoricalSampple)
}
} //End getHeartRateMaxFromWorkouts
It's a good practice to make leave top of callback
guard error == nil else { print("get heart rate error"); dispatchGroup.leave() return ; }
guard let unwrappedResults = results as? [HKQuantitySample] else { print("get heart rate error"); dispatchGroup.leave(); return}
let heartRatesAsDouble = unwrappedResults.map {$0.quantity.doubleValue(for: heartRateUnit)}
guard let max = heartRatesAsDouble.max() else { dispatchGroup.leave(); return }
let maxAsCustomHistoricalSample = CustomHistoricalSample(value: max, date: workout.startDate)
heartRateMaxArrayAsCustomHistoricalSample.append(maxAsCustomHistoricalSample)
let average = heartRatesAsDouble.average
let averageAsCustomHistoricalSample = CustomHistoricalSample(value: average, date: workout.startDate)
dispatchGroup.leave()
heartRateAvgArrayAsCustomHistoricalSample.append(averageAsCustomHistoricalSample)

For iOS healthkit how to save systolic and diastolic blood pressure values?

Here is code for saving blood pressure data in health kit
HKUnit *BPunit = [HKUnit millimeterOfMercuryUnit];
HKQuantity *BPSysQuantity = [HKQuantity quantityWithUnit:BPunit doubleValue:150.0];
HKQuantityType *BPSysType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureSystolic];
HKQuantitySample *BPSysSample = [HKQuantitySample quantitySampleWithType:BPSysType quantity:BpsysQuantity startDate:now endDate:now];
[self.healthStore saveObject:BPSysSample withCompletion:^(BOOL success, NSError *error)
same way for diastolic also,
But how to save both combine as single entry in health app? Currently two diffrent entries are saved for systolic and diastolic blood pressure in health app.
- (void)saveBloodPressureIntoHealthStore:(double)Systolic Dysbp:(double)Diastolic {
HKUnit *BloodPressureUnit = [HKUnit millimeterOfMercuryUnit];
HKQuantity *SystolicQuantity = [HKQuantity quantityWithUnit:BloodPressureUnit doubleValue:Systolic];
HKQuantity *DiastolicQuantity = [HKQuantity quantityWithUnit:BloodPressureUnit doubleValue:Diastolic];
HKQuantityType *SystolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureSystolic];
HKQuantityType *DiastolicType = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierBloodPressureDiastolic];
NSDate *now = [NSDate date];
HKQuantitySample *SystolicSample = [HKQuantitySample quantitySampleWithType:SystolicType quantity:SystolicQuantity startDate:now endDate:now];
HKQuantitySample *DiastolicSample = [HKQuantitySample quantitySampleWithType:DiastolicType quantity:DiastolicQuantity startDate:now endDate:now];
NSSet *objects=[NSSet setWithObjects:SystolicSample,DiastolicSample, nil];
HKCorrelationType *bloodPressureType = [HKObjectType correlationTypeForIdentifier:
HKCorrelationTypeIdentifierBloodPressure];
HKCorrelation *BloodPressure = [HKCorrelation correlationWithType:bloodPressureType startDate:now endDate:now objects:objects];
[self.healthStore saveObject:BloodPressure withCompletion:^(BOOL success, NSError *error) {
if (!success) {
NSLog(#"An error occured saving the height sample %#. In your app, try to handle this gracefully. The error was: %#.", BloodPressure, error);
abort();
}
[_activity stopAnimating];
UIAlertView *savealert=[[UIAlertView alloc]initWithTitle:#"HealthDemo" message:#"Blood Pressure values has been saved to Health App" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[savealert show];
}];
}
In Swift 3:
func saveBloodPressure(systolic systolicValue: Double, diastolic diastolicValue: Double, completion completionBlock: #escaping (Bool, Error?) -> Void) {
let unit = HKUnit.millimeterOfMercury()
let systolicQuantity = HKQuantity(unit: unit, doubleValue: systolicValue)
let diastolicQuantity = HKQuantity(unit: unit, doubleValue: diastolicValue)
let systolicType = HKQuantityType.quantityType(forIdentifier: .bloodPressureSystolic)!
let diastolicType = HKQuantityType.quantityType(forIdentifier: .bloodPressureDiastolic)!
let nowDate = Date()
let systolicSample = HKQuantitySample(type: systolicType, quantity: systolicQuantity, start: nowDate, end: nowDate)
let diastolicSample = HKQuantitySample(type: diastolicType, quantity: diastolicQuantity, start: nowDate, end: nowDate)
let objects: Set<HKSample> = [systolicSample, diastolicSample]
let type = HKObjectType.correlationType(forIdentifier: .bloodPressure)!
let correlation = HKCorrelation(type: type, start: nowDate, end: nowDate, objects: objects)
self.healthKitStore.save(correlation) { (success, error) -> Void in
if !success {
print("An error occured saving the Blood pressure sample \(systolicSample). In your app, try to handle this gracefully. The error was: \(error).")
}
completionBlock(success, error)
}
}
Check out HKCorrelation. A correlation is a set of related objects and is designed to represent things like blood pressure readings and food. You can save create and save correlations just like samples and you can query for correlations using HKCorrelationQuery.
Swift : iOS : Save Blood Pressure:
private func saveBloodPressureIntoHealthStore(bloodPressureValueSystolic:Double
,bloodPressureValueDiastolic:Double) -> Void {
// Save the user's blood pressure into HealthKit.
let bloodPressureUnit: HKUnit = HKUnit.millimeterOfMercuryUnit()
let bloodPressureSystolicQuantity: HKQuantity = HKQuantity(unit: bloodPressureUnit, doubleValue: bloodPressureValueSystolic)
let bloodPressureDiastolicQuantity: HKQuantity = HKQuantity(unit: bloodPressureUnit, doubleValue: bloodPressureValueDiastolic)
let bloodPressureSystolicType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodPressureSystolic)
let bloodPressureDiastolicType: HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodPressureDiastolic)
let nowDate: NSDate = NSDate()
let bloodPressureSystolicSample: HKQuantitySample = HKQuantitySample(type: bloodPressureSystolicType
, quantity: bloodPressureSystolicQuantity, startDate: nowDate, endDate: nowDate)
let bloodPressureDiastolicSample: HKQuantitySample = HKQuantitySample(type: bloodPressureDiastolicType
, quantity: bloodPressureDiastolicQuantity, startDate: nowDate, endDate: nowDate)
let completion: ((Bool, NSError!) -> Void) = {
(success, error) -> Void in
if !success {
println("An error occured saving the Blood pressure sample \(bloodPressureSystolicSample). In your app, try to handle this gracefully. The error was: \(error).")
abort()
}
}// end completion
var objects : NSSet = NSSet(objects: bloodPressureSystolicSample,bloodPressureDiastolicSample)
var bloodPressureType: HKCorrelationType = HKObjectType.correlationTypeForIdentifier(HKCorrelationTypeIdentifierBloodPressure)
var bloodPressureCorrelation : HKCorrelation = HKCorrelation(type: bloodPressureType, startDate: nowDate
, endDate: nowDate, objects: objects)
self.healthStore!.saveObject(bloodPressureCorrelation, withCompletion: completion)
}// end saveBloodPressureIntoHealthStore
Xamarin.iOS solution
public void SaveBloodPressure(DateTime date, double systolic, double diastolic, double beatsPerMinute)
{
using (var healthKitStore = new HKHealthStore())
{
var heartRateUnitType = HKUnit.MillimeterOfMercury;
var diastolicQuantity = HKQuantity.FromQuantity(heartRateUnitType, diastolic);
var diastolicQuantityType = HKQuantityType.GetQuantityType(HKQuantityTypeIdentifierKey.BloodPressureDiastolic);
var diastolicSample = HKQuantitySample.FromType(diastolicQuantityType, diastolicQuantity, date.ToUniversalTime().ToNSDate(), date.ToUniversalTime().ToNSDate(), new HKMetadata());
var systolicQuantity = HKQuantity.FromQuantity(heartRateUnitType, systolic);
var systolicQuantityType = HKQuantityType.GetQuantityType(HKQuantityTypeIdentifierKey.BloodPressureSystolic);
var systolicSample = HKQuantitySample.FromType(systolicQuantityType, systolicQuantity, date.ToUniversalTime().ToNSDate(), date.ToUniversalTime().ToNSDate(), new HKMetadata());
var objects = new NSSet(systolicSample, diastolicSample);
var bloodPressureType = HKCorrelationType.GetCorrelationType(HKCorrelationTypeKey.IdentifierBloodPressure);
var bloodPressure = HKCorrelation.Create(bloodPressureType, date.ToUniversalTime().ToNSDate(), date.ToUniversalTime().ToNSDate(), objects);
try
{
healthKitStore.SaveObject(bloodPressure, (success, error) =>
{
//action to take on success/failure
});
}
catch (Exception)
{
//handle exception
}
try
{
var beatsPerMinuteUnits = HKUnit.Count.UnitDividedBy(HKUnit.Minute);
var beatsPerMinuteQuantity = HKQuantity.FromQuantity(beatsPerMinuteUnits, beatsPerMinute);
var heartRateQuantityType = HKQuantityType.GetQuantityType(HKQuantityTypeIdentifierKey.HeartRate);
var heartRateSample = HKQuantitySample.FromType(heartRateQuantityType, beatsPerMinuteQuantity, date.ToUniversalTime().ToNSDate(), date.ToUniversalTime().ToNSDate(), new HKMetadata());
healthKitStore.SaveObject(heartRateSample, (success, error) =>
{
//handle success / failure
});
}
catch (Exception)
{
//handle exception
}
}
}