Получение вчерашних шагов из HealthKit

Я создаю приложение для личного использования, и в настоящее время я застрял в том, как точно получить вчерашние шаги из комплекта здоровья. А затем оттуда, поместив его в переменную (должно быть легко, я знаю).

У меня есть класс HealthKitManager, который вызывает функцию из представления, а затем добавляет ее к переменной из того же представления.

Я изучил большинство вопросов HealthKit и получил данные, но я не думаю, что это точные данные. Данные моего телефона со вчерашнего дня составляют 1442 шага, но он возвращает 2665 шагов. Кроме того, когда я пытаюсь поместить данные в переменную, она выводит 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)
}}

Вьюконтроллеркласс

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


}

Выход из

print(Player.User.Gold.getSteps(), "IN PLAYER")

is

0 IN PLAYER

Выход из

print("\(steps) STEPS FROM HEALTH KIT")

is

2665.0 STEPS FROM HEALTH KIT

Итак, в основном мои вопросы:

  1. какой NSDate() мне нужен на весь вчерашний день?
  2. как выполнить шаги из вчерашнего шага () и правильно поместить их в переменную в UpdateViewController?

Спасибо за любую помощь!


person impo    schedule 23.09.2016    source источник
comment
Вы подключали какой-либо сторонний трекер к своему телефону?   -  person Umair Afzal    schedule 23.09.2016
comment
Каково ваше фактическое требование? Например, если пользователь вручную добавляет шаги в HealthKit, хотите ли вы также читать их или нет?   -  person Umair Afzal    schedule 23.09.2016
comment
На моем телефоне нет сторонних трекеров, и требуется просто прочитать шаги из приложения для здоровья телефона. Также не требуется ручное добавление шагов   -  person impo    schedule 23.09.2016
comment
Итак, вы хотите получить только шаги, которые были автоматически обнаружены iphone?   -  person Umair Afzal    schedule 23.09.2016
comment
по сути да! Позже, если я захочу сделать его лучше, я попытаюсь использовать API-интерфейс fitbit, но сейчас мне нужно обдумать HealthKit, ага!   -  person impo    schedule 23.09.2016
comment
Я отправил ответ, пожалуйста, проверьте это.   -  person Umair Afzal    schedule 23.09.2016
comment
@UmairAfzal спасибо! Шаги теперь правильно помещаются в переменную! Единственная проблема сейчас заключается в том, что метки, содержащие переменные, не обновляются значениями в представлении (мне нужно вернуться к другому представлению, прежде чем они появятся на метках), что странно, потому что я вызываю функцию, чтобы поместить шаги в сначала переменную, затем пытаюсь обновить метки, но это не работает. У вас есть идеи? Но это кротовина по сравнению с горой, которую вы помогли мне решить, так что спасибо!   -  person impo    schedule 23.09.2016
comment
Пожалуйста, примите мой ответ, если он решил вашу текущую проблему. И опубликуйте новый вопрос для новой проблемы. Спасибо   -  person Umair Afzal    schedule 23.09.2016
comment
Давайте продолжим обсуждение в чате.   -  person Umair Afzal    schedule 23.09.2016
comment
все нормально! Я понял, что это произошло из-за того, что получение данных заняло так много времени, что это было сделано после обновления меток. Я исправил это, обновив метки после паузы. Большое спасибо за вашу помощь!   -  person impo    schedule 23.09.2016
comment
С удовольствием, пожалуйста, примите мой ответ :-)   -  person Umair Afzal    schedule 23.09.2016


Ответы (2)


Это метод, который я использую в своем классе HealthStore.

    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)
}

и вот как я его использую

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)
                })
            }
        })
}

и вот функция, использованная выше для добавленных вручную шагов, которые мы усекаем, чтобы получить точные шаги

    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)
}

Я надеюсь, что это помогает. Дайте мне знать, если вы столкнетесь с какой-либо проблемой.

person Umair Afzal    schedule 23.09.2016
comment
работает идеально! Просто предупреждение для тех, у кого может быть такая же проблема: если у вас есть метки, связанные с переменными, которые обновляются после функции, в которой вы получаете данные, они могут обновиться до завершения запроса, поэтому это будет выглядеть так, как будто он не обновлялся. . У меня сработало добавление NSTimer, который обновлял метки через 2 секунды. - person impo; 23.09.2016
comment
Использование таймера не так хорошо (использование мощности/ЦП), как размещение обновления в блоке завершения запроса — запросы Healthkit принципиально асинхронны, и с этим нужно работать. - person NerdDad; 08.12.2018

Вы можете использовать HKStatisticsQuery.

let quantityType = HKSampleType.quantityType(forIdentifier: .stepCount)!

let predicate = HKQuery.predicateForSamples(
  withStart: startDate,
  end: endDate,
  options: [.strictStartDate, .strictEndDate]
)

let query = HKStatisticsQuery(
  quantityType: quantityType,
  quantitySamplePredicate: predicate,
  options: .cumulativeSum) { (query, result, error) in
    guard let result = result, error == nil else {
      print("HeathService error \(String(describing: error))")
      return
    }

    callback(result)
}
person onmyway133    schedule 17.10.2018