Hi i want to display on my app dayly step count from healthKit
This is my code:
imports
import UIKit
import HealthKit
Class instance
var healthStore = HKHealthStore()
viewDidLoad method
override func viewDidLoad() {
super.viewDidLoad()
if HKHealthStore.isHealthDataAvailable(){
let writeDataTypes = dataTypesToWrite()
let readDataTypes = dataTypesToWrite()
healthStore.requestAuthorization(toShare: writeDataTypes as? Set<HKSampleType>, read: readDataTypes as? Set<HKObjectType>, completion: { (success, error) in
if(!success){
print("error")
return
}
self.updateSteps()
})
}
}
Write:
func dataTypesToWrite() -> NSSet{
let stepsCount = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)
let returnSet = NSSet(objects: stepsCount!)
return returnSet
}
Read:
func dataTypesToRead() -> NSSet{
let stepsCount = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)
let returnSet = NSSet(objects: stepsCount!)
return returnSet
}
Now I want to create func updateSteps()
I have an answer to my question
func updateSteps(completion: #escaping (Double) -> Void) {
let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let startOfDay = Calendar.current.startOfDay(for: now)
let predicate = HKQuery.predicateForSamples(withStart: startOfDay, 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)
}
}
healthStore.execute(query)
}
Related
I have written a function that is supposed to get the user's steps by using HealthKit. Before I added the code that checks no data was manually added, it worked. After adding the new code (compoundPredicate) it does not calculate steps. Any ideas on what to change to make this work?
Here is the file:
import Foundation
import HealthKit
extension Date {
static func mondayAt12AM() -> Date {
return Calendar(identifier: .iso8601).date(from: Calendar(identifier: .iso8601).dateComponents([.yearForWeekOfYear, .weekOfYear], from: Date()))!
}
}
class HealthStore {
var healthStore: HKHealthStore?
var query: HKStatisticsCollectionQuery?
init() {
if HKHealthStore.isHealthDataAvailable() {
healthStore = HKHealthStore()
}
}
func calculateSteps(completion: #escaping (HKStatisticsCollection?)-> Void) {
let stepType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!
let startDate = Calendar.current.date(byAdding: .day, value: -7, to: Date())
let anchorDate = Date.mondayAt12AM()
let daily = DateComponents(day: 1)
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: .strictStartDate)
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates:
[.init(format: "metadata.%K == NO", HKMetadataKeyWasUserEntered), predicate]
)
query = HKStatisticsCollectionQuery(
quantityType: stepType,
quantitySamplePredicate: compoundPredicate,
options: .cumulativeSum,
anchorDate: anchorDate,
intervalComponents: daily)
query!.initialResultsHandler = { query, statisticsCollection, error in
completion(statisticsCollection)
}
if let healthStore = healthStore, let query = self.query {
healthStore.execute(query)
}
}
func requestAuthorization(completion: #escaping (Bool) -> Void) {
let stepType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!
guard let healthStore = self.healthStore else { return completion(false) }
healthStore.requestAuthorization(toShare: [], read: [stepType]) { (success, error) in
completion(success)
}
}
}
I think HKMetadataKeyWasUserEntered is simply absent from many or most HealthKit samples, instead of being present with a value of NO.
Try this instead for the predicate format parameter: "metadata.%K != YES"
I'm trying to get steps from half an hour ago and I'm using the method discussed here. The following is my code:
func getSteps(completion: #escaping (Double) -> Void) {
let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let calendar = Calendar.current
let halfHourAgoDate = calendar.date(byAdding: .minute, value: -30, to: now)
if let date = halfHourAgoDate {
let predicate = HKQuery.predicateForSamples(withStart: date, 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 {
completion(resultCount)
return
}
if let sum = result.sumQuantity() {
resultCount = sum.doubleValue(for: HKUnit.count())
return
}
DispatchQueue.main.async {
completion(resultCount)
}
}
healthStore.execute(query)
} else {
print("Error MainVC, date is being cast as nil")
}
}
When I actually try to get steps, this is my code:
var todaysSteps: Double = 0
getSteps(completion: { (resultCount) -> Void in
todaysSteps = resultCount
})
print(todaysSteps) // always comes out to 0.0
Yet whenever I run this code, the code returns 0.0. I've enabled HealthKit, and authorized it for steps, so I'm not sure what the reason is. Also note this is in my variable declarations:
let healthStore = HKHealthStore()
For anyone encountering the same issue, you need to do the code inside the completion handler, so:
var todaysSteps: Double = 0
getSteps(completion: { (resultCount) -> Void in
print(resultCount)
})
I am trying to create a label which displays today's steps from HealthKit. I have got as far as allowing HealthKit to read/write data, but I'm stuck on actually obtaining the data.
Can anyone offer any advice? Here's my code, including the label I want to display the steps:
import Foundation
import UIKit
import HealthKit
class HealthKitPage : UIViewController
{
let healthStore: HKHealthStore = HKHealthStore()
override func viewDidLoad()
{
var shareTypes = Set<HKSampleType>()
shareTypes.insert(HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!)
var readTypes = Set<HKObjectType>()
readTypes.insert(HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!)
healthStore.requestAuthorization(toShare: shareTypes, read: readTypes) { (success, error) -> Void in
if success {
print("success")
} else {
print("failure")
}
if let error = error { print(error) }
}
}
#IBOutlet weak var StepCount: UILabel!
}
You can fetch today's steps using HKStatisticsQuery. You can now in your view controller use getTodaysSteps method and set your label's text property.
Swift 3.1:
let healthStore = HKHealthStore()
func getTodaysSteps(completion: #escaping (Double) -> Void) {
let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let startOfDay = Calendar.current.startOfDay(for: now)
let predicate = HKQuery.predicateForSamples(withStart: startOfDay, 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 {
log.error("Failed to fetch steps = \(error?.localizedDescription ?? "N/A")")
completion(resultCount)
return
}
if let sum = result.sumQuantity() {
resultCount = sum.doubleValue(for: HKUnit.count())
}
DispatchQueue.main.async {
completion(resultCount)
}
}
healthStore.execute(query)
}
You can call this method like this:
override func viewDidLoad() {
super.viewDidLoad()
getTodaysStepCount { [weak self] steps in
self?.StepCount.text = "\(steps)"
}
}
I am getting today steps from healthkit using below code.
func retrieveStepCount(completion: (stepRetrieved: Double) -> Void) {
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(NSDate())
let predicate = HKQuery.predicateForSamplesWithStartDate(newDate, endDate: NSDate(), options: .None) // Our search predicate which will fetch all steps taken today
let interval: NSDateComponents = NSDateComponents()
interval.day = 1
let query = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: predicate, options: .CumulativeSum, anchorDate: newDate as NSDate, intervalComponents:interval as NSDateComponents)
query.initialResultsHandler = { query, results, error in
if error != nil {
print("Something went Wrong")
return
}
if let myResults = results{
myResults.enumerateStatisticsFromDate(newDate, toDate: NSDate()) {
statistics, stop in
if let quantity = statistics.sumQuantityForSource(HKSource.defaultSource()) {
let steps = quantity.doubleValueForUnit(HKUnit.countUnit())
print("Steps = \(Int(steps))")
completion(stepRetrieved: steps)
}
}
}
}
executeQuery(query)
}
Now lets say I have these steps in total
From which I have some steps which were auto detected by device. and some were added by some other application to heathkit.
I do want both of them and I m getting both of them but the problem comes when user some some manuall steps to the healthkit.
I do not want to get these manually added steps. So basically I want to get (5,793 - 2300) = 3493 steps.
How can I do that ? I have tried to get name of HKSource I do know that when user enter steps manually, name of the source is "Health" but how do I filter steps on this base ? Please guide me about this and what am I missing here ? Thanks in advance
This might not be the best solution, But I believe it will work. What you can do is get all the steps which were added manually using HKSampleQuery. here is an example.
func todayManuallyAddedStepsSteps(completion: (Double, NSError?) -> () )
{
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting
let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)
let predicate = HKQuery.predicateForSamplesWithStartDate(newDate, endDate: NSDate(), options: .None) // Our search predicate which will fetch all steps taken today
// The actual HealthKit Query which will fetch all of the steps and add them up for us.
let query = HKSampleQuery(sampleType: type!, predicate: predicate, limit: 0, sortDescriptors: nil) { query, results, error in
var steps: Double = 0
if results?.count > 0
{
for result in results as! [HKQuantitySample]
{
print("Steps \(result.quantity.doubleValueForUnit(HKUnit.countUnit()))")
print()
// checking and adding manually added steps
if result.sourceRevision.source.name == "Health" {
// these are manually added steps
print(result.sourceRevision.source.name)
print("Steps \(result.quantity.doubleValueForUnit(HKUnit.countUnit()))")
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
else{
// these are auto detected steps which we do not want from using HKSampleQuery
}
}
print(steps)
}
completion(steps, error)
}
executeQuery(query)
}
and then get the today total steps using HKStatisticsCollectionQuery like below
func TodayTotalSteps(completion: (stepRetrieved: Double) -> Void) {
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting
let calendar = NSCalendar.currentCalendar()
let interval = NSDateComponents()
interval.day = 1
let anchorComponents = calendar.components([.Day , .Month , .Year], fromDate: NSDate())
anchorComponents.hour = 0
let anchorDate = calendar.dateFromComponents(anchorComponents)
let stepsQuery = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: nil, options: .CumulativeSum, anchorDate: anchorDate!, intervalComponents: interval)
stepsQuery.initialResultsHandler = {query, results, error in
let endDate = NSDate()
let startDate = calendar.dateByAddingUnit(.Day, value: 0, toDate: endDate, options: [])
if let myResults = results{ myResults.enumerateStatisticsFromDate(startDate!, toDate: endDate) { statistics, stop in
if let quantity = statistics.sumQuantity(){
let date = statistics.startDate
let steps = quantity.doubleValueForUnit(HKUnit.countUnit())
print("\(date): steps = \(steps)")
completion(stepRetrieved: steps)
}
}
}
}
executeQuery(stepsQuery)
}
Now you can call these methods and subtract manually added steps like below
todayManuallyAddedSteps({ (steps , error) in
if error != nil{
print(error)
}
else{
// truncating manuall steps
TodayTotalSteps({ (stepRetrieved) in
// steps without manuall steps
print(Int(stepRetrieved - steps))
})
}
})
You should create HKSourceQuery predicate and compound it with predicate you want to use. In my case i did
fileprivate static func configureSourcePredicate(identifier: HKQuantityTypeIdentifier, completion: #escaping(NSPredicate?) -> Void) {
var deviceSources : Set<HKSource> = Set()
let appleHealth = "com.apple.health"
let handler : (HKSourceQuery, Set<HKSource>?, Error?) -> Void = { query , sources , error in
if sources == nil || error != nil {
completion(nil)
return
}
for source in sources! {
if source.bundleIdentifier.hasPrefix(appleHealth){
deviceSources.insert(source)
}
}
completion(HKQuery.predicateForObjects(from: deviceSources))
}
let sampleType = HKQuantityType.quantityType(forIdentifier: identifier)
let sourceQuery = HKSourceQuery(sampleType: sampleType!, samplePredicate: nil, completionHandler: handler)
healthStore?.execute(sourceQuery)
}
fileprivate static func request(type identifier: HKQuantityTypeIdentifier, startDate : Date, interval : DateComponents, completion : #escaping(TYPE_OF_DATA) -> Void) {
configureSourcePredicate(identifier: identifier, completion: { sourcePredicate in
let type = HKSampleType.quantityType(forIdentifier: identifier)
var predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: [.strictStartDate, .strictEndDate])
if sourcePredicate != nil {
predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate , sourcePredicate!])
}
let query = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: predicate, options: [.cumulativeSum], anchorDate: Date(), intervalComponents: interval)
query.initialResultsHandler = { query, results, error in
if error != nil || results == nil {
return
}
//your code here
DispatchQueue.main.async {
completion(RETURNED_DATA)
}
}
healthStore?.execute(query)
})
}
First Configure the HKSources, which indicates where we have to get the health data. (com.apple.health, com.apple.Health ...)
func configureSourcePredicate() {
self.queryGroup = DispatchGroup()
self.deviceSources = Set()
//Only get the health data from the apple health without manually added steps
let appleHealth = "com.apple.health"
self.queryGroup?.enter()
let sourcesQueryCompletionHandler : CompletionBlock = { (query , sources , error) in
s
if let deviceSourcesLocal = sources {
for source in deviceSourcesLocal {
if source.bundleIdentifier.hasPrefix(appleHealth){
self.deviceSources?.insert(source)
print("Device sources are \(source.bundleIdentifier)")
}
}
self.queryGroup?.leave()
}
}
let sampleType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)
let sourceQuery = HKSourceQuery(sampleType: sampleType!, samplePredicate: nil, completionHandler: sourcesQueryCompletionHandler)
self.healthStore.execute(sourceQuery)
self.queryGroup?.notify(queue: DispatchQueue.global(), execute: {
self.todayTotalSteps()
}) }
//Step count access from the data sources
func todayTotalSteps() {
let completionHandler: (HKStatisticsQuery, HKStatistics?, Error?) -> Void = {
(_query, result, error) -> Void in
DispatchQueue.main.async {
print("Result is \(result)")
if let quantity: HKQuantity = result?.sumQuantity() {
let steps = quantity.doubleValue(for: HKUnit.count())
print("Steps = \(steps)")
self.stepsCount.text = "\(steps)"
self.queryGroup?.leave()
}
}
}
let stepsCount = HKQuantityType.quantityType(forIdentifier: .stepCount)
let predicate = predicateForSamplesToday()
self.queryGroup?.enter()
let query = HKStatisticsQuery(quantityType: stepsCount!, quantitySamplePredicate: predicate, options: HKStatisticsOptions.cumulativeSum, completionHandler: completionHandler)
if (self.healthStore) != nil {
self.healthStore.execute(query)
}}
//Creating predicate
private func predicateForSamplesToday() -> NSPredicate
{
print("Device sources \(self.deviceSources)")
let (starDate, endDate): (Date, Date) = self.datesFromToday()
var predicate: NSPredicate = HKQuery.predicateForSamples(withStart: starDate, end: endDate, options: HKQueryOptions.strictStartDate)
if deviceSources == self.deviceSources {
predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate , HKQuery.predicateForObjects(from: deviceSources!)])
}
return predicate
}
I'm using the following Swift code.
let sampleType : HKSampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)
let nowDate: NSDate = NSDate()
var calendar: NSCalendar = NSCalendar.autoupdatingCurrentCalendar()
let yearMonthDay: NSCalendarUnit = NSCalendarUnit.YearCalendarUnit | NSCalendarUnit.MonthCalendarUnit | NSCalendarUnit.DayCalendarUnit
var components: NSDateComponents = calendar.components(yearMonthDay , fromDate: nowDate)
var beginOfDay : NSDate = calendar.dateFromComponents(components)!
var predicate : NSPredicate = HKQuery.predicateForSamplesWithStartDate(beginOfDay, endDate: nowDate, options: HKQueryOptions.StrictStartDate)
let squery: HKStatisticsQuery = HKStatisticsQuery(quantityType: sampleType, quantitySamplePredicate: predicate, options: HKStatisticsOptions.None) { (qurt, resul, errval) -> Void in
dispatch_async( dispatch_get_main_queue(), { () -> Void in
var quantity : HKQuantity = result.averageQuantity;
var beats : double = quantity.doubleValueForUnit(HKUnit.heartBeatsPerMinuteUnit())
// [quantity doubleValueForUnit:[HKUnit heartBeatsPerMinuteUnit]];
self.txtfldHeartRate.text = "\(beats)"
})
}
healthManager.healthKitStore.executeQuery(squery)
I get the following error message:
Cannot find an initializer for type 'HKStatisticsQuery' that accepts an argument list of type '(quantityType: HKSampleType, quantitySamplePredicate: NSPredicate, options: HKStatisticsOptions, (_, _, _) -> Void)'
Please advise me how to resolve this issue.
Reading data from ViewController (NOT apple watch extension)
Swift 5
let health: HKHealthStore = HKHealthStore()
let heartRateUnit:HKUnit = HKUnit(from: "count/min")
let heartRateType:HKQuantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)!
var heartRateQuery:HKSampleQuery?
/*Method to get todays heart rate - this only reads data from health kit. */
func getTodaysHeartRates() {
//predicate
let calendar = NSCalendar.current
let now = NSDate()
let components = calendar.dateComponents([.year, .month, .day], from: now as Date)
guard let startDate:NSDate = calendar.date(from: components) as NSDate? else { return }
var dayComponent = DateComponents()
dayComponent.day = 1
let endDate:NSDate? = calendar.date(byAdding: dayComponent, to: startDate as Date) as NSDate?
let predicate = HKQuery.predicateForSamples(withStart: startDate as Date, end: endDate as Date?, options: [])
//descriptor
let sortDescriptors = [
NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
]
heartRateQuery = HKSampleQuery(sampleType: heartRateType, predicate: predicate, limit: 25, sortDescriptors: sortDescriptors, resultsHandler: { (query, results, error) in
guard error == nil else { print("error"); return }
self.printHeartRateInfo(results: results)
}) //eo-query
health.execute(heartRateQuery!)
}//eom
/*used only for testing, prints heart rate info */
private func printHeartRateInfo(results:[HKSample]?)
{
for (_, sample) in results!.enumerated() {
guard let currData:HKQuantitySample = sample as? HKQuantitySample else { return }
print("[\(sample)]")
print("Heart Rate: \(currData.quantity.doubleValue(for: heartRateUnit))")
print("quantityType: \(currData.quantityType)")
print("Start Date: \(currData.startDate)")
print("End Date: \(currData.endDate)")
print("Metadata: \(currData.metadata)")
print("UUID: \(currData.uuid)")
print("Source: \(currData.sourceRevision)")
print("Device: \(currData.device)")
print("---------------------------------\n")
}//eofl
}//eom
Swift 3
let health: HKHealthStore = HKHealthStore()
let heartRateUnit:HKUnit = HKUnit(fromString: "count/min")
let heartRateType:HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!
var heartRateQuery:HKSampleQuery?
/*Method to get todays heart rate - this only reads data from health kit. */
func getTodaysHeartRates()
{
//predicate
let calendar = NSCalendar.currentCalendar()
let now = NSDate()
let components = calendar.components([.Year,.Month,.Day], fromDate: now)
guard let startDate:NSDate = calendar.dateFromComponents(components) else { return }
let endDate:NSDate? = calendar.dateByAddingUnit(.Day, value: 1, toDate: startDate, options: [])
let predicate = HKQuery.predicateForSamplesWithStartDate(startDate, endDate: endDate, options: .None)
//descriptor
let sortDescriptors = [
NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
]
heartRateQuery = HKSampleQuery(sampleType: heartRateType,
predicate: predicate,
limit: 25,
sortDescriptors: sortDescriptors)
{ (query:HKSampleQuery, results:[HKSample]?, error:NSError?) -> Void in
guard error == nil else { print("error"); return }
//self.printHeartRateInfo(results)
self.updateHistoryTableViewContent(results)
}//eo-query
health.executeQuery(heartRateQuery!)
}//eom
/*used only for testing, prints heart rate info */
private func printHeartRateInfo(results:[HKSample]?)
{
for(var iter = 0 ; iter < results!.count; iter++)
{
guard let currData:HKQuantitySample = results![iter] as? HKQuantitySample else { return }
print("[\(iter)]")
print("Heart Rate: \(currData.quantity.doubleValueForUnit(heartRateUnit))")
print("quantityType: \(currData.quantityType)")
print("Start Date: \(currData.startDate)")
print("End Date: \(currData.endDate)")
print("Metadata: \(currData.metadata)")
print("UUID: \(currData.UUID)")
print("Source: \(currData.sourceRevision)")
print("Device: \(currData.device)")
print("---------------------------------\n")
}//eofl
}//eom
Reading data with apple watch extension:
to execute query in apple watch, do the following:
heartRateQuery = self.createStreamingQuery()
health.executeQuery(heartRateQuery!)
don't forget the properties:
let health: HKHealthStore = HKHealthStore()
let heartRateUnit:HKUnit = HKUnit(fromString: "count/min")
let heartRateType:HKQuantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!
var heartRateQuery:HKQuery?
/*The below methods has no Limit, query for heart infinitely once its the query its executed */
private func createStreamingQuery() -> HKQuery
{
let queryPredicate = HKQuery.predicateForSamplesWithStartDate(NSDate(), endDate: nil, options: .None)
let query:HKAnchoredObjectQuery = HKAnchoredObjectQuery(type: self.heartRateType, predicate: queryPredicate, anchor: nil, limit: Int(HKObjectQueryNoLimit))
{ (query:HKAnchoredObjectQuery, samples:[HKSample]?, deletedObjects:[HKDeletedObject]?, anchor:HKQueryAnchor?, error:NSError?) -> Void in
if let errorFound:NSError = error
{
print("query error: \(errorFound.localizedDescription)")
}
else
{
//printing heart rate
if let samples = samples as? [HKQuantitySample]
{
if let quantity = samples.last?.quantity
{
print("\(quantity.doubleValueForUnit(heartRateUnit))")
}
}
}
}//eo-query
query.updateHandler =
{ (query:HKAnchoredObjectQuery, samples:[HKSample]?, deletedObjects:[HKDeletedObject]?, anchor:HKQueryAnchor?, error:NSError?) -> Void in
if let errorFound:NSError = error
{
print("query-handler error : \(errorFound.localizedDescription)")
}
else
{
//printing heart rate
if let samples = samples as? [HKQuantitySample]
{
if let quantity = samples.last?.quantity
{
print("\(quantity.doubleValueForUnit(heartRateUnit))")
}
}
}//eo-non_error
}//eo-query-handler
return query
}//eom
How to request authorization?
func requestAuthorization()
{
//reading
let readingTypes:Set = Set( [heartRateType] )
//writing
let writingTypes:Set = Set( [heartRateType] )
//auth request
health.requestAuthorizationToShareTypes(writingTypes, readTypes: readingTypes) { (success, error) -> Void in
if error != nil
{
print("error \(error?.localizedDescription)")
}
else if success
{
}
}//eo-request
}//eom
HKStatisticsQuery would not be my first choice for that. It is used for statistical calculations (i.e. minimum, maximum, average, sum).
You can use a simple HKQuery:
public func fetchLatestHeartRateSample(
completion: #escaping (_ samples: [HKQuantitySample]?) -> Void) {
/// Create sample type for the heart rate
guard let sampleType = HKObjectType
.quantityType(forIdentifier: .heartRate) else {
completion(nil)
return
}
/// Predicate for specifiying start and end dates for the query
let predicate = HKQuery
.predicateForSamples(
withStart: Date.distantPast,
end: Date(),
options: .strictEndDate)
/// Set sorting by date.
let sortDescriptor = NSSortDescriptor(
key: HKSampleSortIdentifierStartDate,
ascending: false)
/// Create the query
let query = HKSampleQuery(
sampleType: sampleType,
predicate: predicate,
limit: Int(HKObjectQueryNoLimit),
sortDescriptors: [sortDescriptor]) { (_, results, error) in
guard error == nil else {
print("Error: \(error!.localizedDescription)")
return
}
completion(results as? [HKQuantitySample])
}
/// Execute the query in the health store
let healthStore = HKHealthStore()
healthStore.execute(query)
}
Specifying types of constants and variables during an initialization is often redundant in Swift like in your case you has specified a parent HKSampleType type instead of its subclass HKQuantityType. So in your case just omit types declaration:
let sampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!
let nowDate = NSDate()
var calendar = NSCalendar.autoupdatingCurrentCalendar()
If you use Swift 2.0 you should also utilize array like syntax in the next line:
let yearMonthDay: NSCalendarUnit = [NSCalendarUnit.Year, NSCalendarUnit.Month, NSCalendarUnit.Day]
Try this way, moving in the completion handler seems to resolves this for me in Xcode 6.4-
let squery = HKStatisticsQuery(quantityType: sampleType, quantitySamplePredicate: predicate, options: HKStatisticsOptions.None, completionHandler: { (qurt, result, errval) -> Void in
dispatch_async( dispatch_get_main_queue(), { () -> Void in
var quantity : HKQuantity = result.averageQuantity();
var beats : Double = quantity.doubleValueForUnit(HKUnit.atmosphereUnit())
// [quantity doubleValueForUnit:[HKUnit heartBeatsPerMinuteUnit]];
})
})
Note: I was seeing some compiler errors within closure, so have changed 2 lines to just ensure compilation-
var quantity : HKQuantity = result.averageQuantity();
var beats : Double = quantity.doubleValueForUnit(HKUnit.atmosphereUnit())
You should be using HKQuantityType instead of HKSampleType in your query:
let squery: HKStatisticsQuery = HKStatisticsQuery(quantityType: HKQuantityTypeIdentifierHeartRate, quantitySamplePredicate: predicate, options: HKStatisticsOptions.None) { (qurt, resul, errval) -> Void in