I'm building an app for personal use, and I am currently stuck on how to accurately get yesterdays steps from the healthkit. And then from there, placing it into a variable (should be easy, I know).
I have a HealthKitManager class that calls the function from inside a view, and then appends that to a variable from that same view.
I have scoured most of the healthKit questions, and I get back data, but I don't think it is accurate data. My phone data from yesterday is 1442 steps, but it's returning 2665 steps. On top of that, when I try to put the data is a variable it prints out as 0.
HealthKitManagerClass
import Foundation
import HealthKit
class HealthKitManager {
let storage = HKHealthStore()
init()
{
checkAuthorization()
}
func checkAuthorization() -> Bool
{
// Default to assuming that we're authorized
var isEnabled = true
// Do we have access to HealthKit on this device?
if HKHealthStore.isHealthDataAvailable()
{
// We have to request each data type explicitly
let steps = NSSet(object: HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!)
// Now we can request authorization for step count data
storage.requestAuthorizationToShareTypes(nil, readTypes: steps as? Set<HKObjectType>) { (success, error) -> Void in
isEnabled = success
}
}
else
{
isEnabled = false
}
return isEnabled
}
func yesterdaySteps(completion: (Double, NSError?) -> ())
{
// The type of data we are requesting (this is redundant and could probably be an enumeration
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
// Our search predicate which will fetch data from now until a day ago
// (Note, 1.day comes from an extension
// You'll want to change that to your own NSDate
let calendar = NSCalendar.currentCalendar()
let yesterday = calendar.dateByAddingUnit(.Day, value: -1, toDate: NSDate(), options: [])
//this is probably why my data is wrong
let predicate = HKQuery.predicateForSamplesWithStartDate(yesterday, endDate: NSDate(), options: .None)
// The actual HealthKit Query which will fetch all of the steps and sub 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]
{
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
}
//I'm unsure if this is correct as well
completion(steps, error)
print("\(steps) STEPS FROM HEALTH KIT")
//this adds the steps to my character (is this in the right place)
Player.User.Gold.addSteps(Int(steps))
}
//not 100% on what this does, but I know it is necessary
storage.executeQuery(query)
}}
ViewControllerClass
import UIKit
import Foundation
class UpdateViewController: UIViewController {
@IBOutlet var back: UIButton!
let HKM = HealthKitManager()
var stepsFromPhone = Double()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
back.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2))
HKM.yesterdaySteps(){ steps, error in
self.stepsFromPhone = steps
}
Player.User.Gold.addSteps(Int(stepsFromPhone))
print(Player.User.Gold.getSteps(), "IN PLAYER")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The output from
print(Player.User.Gold.getSteps(), "IN PLAYER")
is
0 IN PLAYER
The output from
print("\(steps) STEPS FROM HEALTH KIT")
is
2665.0 STEPS FROM HEALTH KIT
so, basically my questions are:
Thank you for any help!
This is the method I am using in my healthStore class
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()
var steps = 0.0
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
steps = quantity.doubleValueForUnit(HKUnit.countUnit())
// print("\(date): steps = \(steps)")
}
completion(stepRetrieved: steps)
}
} else {
completion(stepRetrieved: steps)
}
}
executeQuery(stepsQuery)
}
and here is How I am using it
func getStepsData() {
// I am sendng steps to my server thats why using this variable
var stepsToSend = 0
MyHealthStore.sharedHealthStore.todayManuallyAddedSteps({ (steps , error) in
if error != nil{
// handle error
}
else{
// truncating manuall steps
MyHealthStore.sharedHealthStore.TodayTotalSteps({ (stepRetrieved) in
stepsToSend = Int(stepRetrieved - steps)
})
}
})
}
and here is the function used above for manually added steps which we are truncating in order to get exact steps
func todayManuallyAddedSteps(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]
{
// checking and adding manually added steps
if result.sourceRevision.source.name == "Health" {
// these are manually added steps
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
else{
// these are auto detected steps which we do not want from using HKSampleQuery
}
}
completion(steps, error)
} else {
completion(steps, error)
}
}
executeQuery(query)
}
I hope it helps. Let me know if you face any issue.