I want to get the step data of a user by using HealthKit. However, I want to make sure that data that has been entered manually is ignored. I found a solution where someone asked this question and I know what needs to be entered but I am unsure how to do this. Wherever I put this code I seem to get errors:
Here is my code:
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)
query = HKStatisticsCollectionQuery(quantityType: stepType, quantitySamplePredicate: predicate, 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)
}
}
And this is the sort of code I need to implement in the predicate part.
let predicate = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered)
Where shall I put this code?
UPDATED:
I have the code running with no errors however when the function runs it comes out as 0. Any ideas?
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)
}
}
}
Create a compound predicate with your current predicate
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: compountPredicate,
options: .cumulativeSum,
anchorDate: anchorDate,
intervalComponents: daily)
I would try to create an NSCompoundPredicate with both the predicates you want and use that while creating the query.
If you are still running into errors, please share the specific errors you see.
You need to create one Compound Predicate with the current Predicate as sub Predicate. And in format set metadata.%K != YES (metadata.%K == NO will not work here, because the value for the key could be YES, NO, or nil and nil implies NO).
And then create a query using that Compound Predicate.
Here is the code Sample
let stepType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount)!
let startDate = Calendar.current.date(byAdding: .day, value: -7, to: Date())!
let daily = DateComponents(day: 1)
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: .strictStartDate)
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [.init(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered), predicate])
let query = HKStatisticsCollectionQuery(quantityType: stepType, quantitySamplePredicate: compoundPredicate, options: .cumulativeSum, anchorDate: startDate, intervalComponents: daily)
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 can able to get my health data list from health kit. I used below code to get my today's step count :
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, _ in
guard let result = result, let sum = result.sumQuantity() else {
completion(0.0)
return
}
completion(sum.doubleValue(for: HKUnit.count()))
}
healthStore.execute(query)
}
But i have one more health data of my brother, where he invited me to see his health data in my real device. Now i was not able to read / get his health data. Is there any way to get that.
Any solution would be great ...!
You don't want a statics query if you want the step count. Try this;
let comp: DateComponents = Calendar.current.dateComponents([.year, .month], from: Date())
let startOfMonth = Calendar.current.date(from: comp)!
searchPredicate = HKQuery.predicateForSamples(withStart: startOfMonth, end: Date(), options: .strictStartDate)
let sampleQuery = HKSampleQuery(sampleType: .stepCount, predicate: searchPredicate, limit: limit, sortDescriptors: [])
{
(query, result, error) in
if error != nil
{
completion(-1)
}
self.hkSampleRecs.removeAll(keepingCapacity: true)
if result != nil
{
for r in result!
{
self.hkSampleRecs.append(r)
}
}
completion(self.hkSampleRecs.count)
}
healthStore.execute(sampleQuery)
Note that I haven't shown you the completion setup I used. I'm also storing the records in an array I defined elsewhere (hkSampleRecs). This example also gives data only for this month so far. Change the dates as you need.
I'm trying to obtain the steps from the last 7 days, but I could not find how to do it. What i would like to receive is an array of 7 elements in which every element is a Day with it respectives total steps. I currently have this code, which obtains today's steps:
//Gets the steps
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
guard let result = result, let sum = result.sumQuantity() else {
print("Failed to fetch steps = \(error?.localizedDescription ?? "N/A")")
completion(0.0)
return
}
DispatchQueue.main.async {
completion(sum.doubleValue(for: HKUnit.count()))
}
}
healthKitStore.execute(query)
}
And I call the function like this:
getTodaysSteps { (steps) in
self.stepsNumber = Int(steps)
}
Try using HKStatisticsCollectionQuery, which will do the date math for you and bucket the results automatically. Here's an example that should provide the step counts for the last 7 days:
let stepsQuantityType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
let now = Date()
let exactlySevenDaysAgo = Calendar.current.date(byAdding: DateComponents(day: -7), to: now)!
let startOfSevenDaysAgo = Calendar.current.startOfDay(for: exactlySevenDaysAgo)
let predicate = HKQuery.predicateForSamples(withStart: startOfSevenDaysAgo, end: now, options: .strictStartDate)
let query = HKStatisticsCollectionQuery.init(quantityType: stepsQuantityType,
quantitySamplePredicate: predicate,
options: .cumulativeSum,
anchorDate: startOfSevenDaysAgo,
intervalComponents: DateComponents(day: 1))
query.initialResultsHandler = { query, results, error in
guard let statsCollection = results else {
// Perform proper error handling here...
}
statsCollection.enumerateStatistics(from: startOfSevenDaysAgo, to: now) { statistics, stop in
if let quantity = statistics.sumQuantity() {
let stepValue = quantity.doubleValueForUnit(HKUnit.countUnit())
// ...
}
}
}
There's even more simpler solution here.
func getTotalSteps(forPast days: Int, completion: #escaping (Double) -> Void) {
// Getting quantityType as stepCount
guard let stepsQuantityType = HKObjectType.quantityType(forIdentifier: .stepCount) else {
print("*** Unable to create a step count type ***")
return
}
let now = Date()
let startDate = Calendar.current.date(byAdding: DateComponents(day: -days), to: now)
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: now, options: .strictStartDate)
let query = HKStatisticsQuery(quantityType: stepsQuantityType, quantitySamplePredicate: predicate, options: .cumulativeSum) { _, result, _ in
guard let result = result, let sum = result.sumQuantity() else {
completion(0.0)
return
}
completion(sum.doubleValue(for: HKUnit.count()))
}
execute(query)
}
Now to call just use the following code:
HKHealthStore().getTotalSteps(forPast: 30) { totalSteps in
print(totalSteps)
}
The only change you have to implement is to change the Date object you supply as the startWith parameter to your HKStatisticsQuery. You can create a Date object representing the start of the day 7 days ago by first going back exactly 7 days in time using Calendar.date(byAdding:,to:), then calling startOfDay(for:) on that object.
let now = Date()
let exactlySevenDaysAgo = Calendar.current.date(byAdding: DateComponents(day: -7), to: now)!
let startOfSevenDaysAgo = Calendar.current.startOfDay(for: exactlySevenDaysAgo)
let predicate = HKQuery.predicateForSamples(withStart: startOfSevenDaysAgo, end: now, options: .strictStartDate)
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