Search code examples
iosswiftswift2healthkithksamplequery

Getting yesterdays steps from HealthKit


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:

  1. what NSDate() do I need for the whole of yesterday?
  2. how do I take the steps from the yesterdaySteps() and correctly place them into a variable in the UpdateViewController?

Thank you for any help!


Solution

  • 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.