如何在Apple Watch iOS中获取当前的StandHour值?

5
我想要获取一个值,该值指示用户是否在这个小时内站立。我还想能够检索当天的StandHours计数。

以下是我一直在尝试理解的苹果链接,以便从HealthKit中获得值。我提供这些链接来帮助理解我正在寻找什么,也帮助你回答我的问题。

Bruno的答案只是答案的一半。例如,他的standUnit变量是他如何拉取用户今天站立的小时数。我测试过了。此外,我假设它必须从summaries变量的范围内拉取。

我在StackOverflow上找到了另一个问题,可能提供一些线索。我认为他们通过HKCategoryTypeIdentifier成功地拉取了一个值:Watch os 2.0 beta: access heart beat rate

这是我尝试的代码:

import UIKit
import HealthKit
import HealthKitUI

class ViewController: UIViewController {

let hkStoreOnVC : HKHealthStore = HKHealthStore()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    authorizeHealthKit()
    hkTest()
    hkTest2()
}

func authorizeHealthKit() {   //-> Bool {
    print("health kit authorize?")
    
    let healthStore = HKHealthStore()
    
    let objectTypes: Set<HKObjectType> = [
        HKObjectType.activitySummaryType()
    ]
    
    healthStore.requestAuthorization(toShare: nil, read: objectTypes) { (success, error) in
        
        // Authorization request finished, hopefully the user allowed access!
        print("health kit authorized")
    }
}

func hkTest() {
    print("health kit test.")
    
    let calendar = Calendar.autoupdatingCurrent
    
    var dateComponents = calendar.dateComponents(
        [ .year, .month, .day ],
        from: Date()
    )
    
    // This line is required to make the whole thing work
    dateComponents.calendar = calendar
    
    let predicate = HKQuery.predicateForActivitySummary(with: dateComponents)
    
    //----------------------
    
    let query = HKActivitySummaryQuery(predicate: predicate) { (query, summaries, error) in
        print("query")
        guard let summaries = summaries, summaries.count > 0
            else {
                print("no summaries")
                return
        }
        
        // Handle data
        
        for thisSummary in summaries {
            //                print("for each summary")
            let standUnit = HKUnit.count()
            let standHours = thisSummary.appleStandHours.doubleValue(for: standUnit)
            print("stand hours \(standHours)")
        }//end for
    } //end query
}

func hkTest2() {
    var isEnabled = true
    print ("authorize health kit" )
    if HKHealthStore.isHealthDataAvailable() {
        let stepsCount  = NSSet(objects: HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.stepCount ) )
        
        for thisValue in stepsCount {
            //              thisValue.
            print("thisValue: \(thisValue)")
        }

        print(" authorize HK - steps count \(stepsCount) ")
    }
    
    // Create the date components for the predicate
    guard let calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian) else {
        fatalError("*** This should never fail. ***")
    }
    
    let endDate = NSDate()
    
    guard let startDate = calendar.date(byAdding: .day, value: -7, to: endDate as Date, options: []) else {
        fatalError("*** unable to calculate the start date ***")
    }
    
    let units: NSCalendar.Unit = [.day, .month, .year, .era]
    
    var startDateComponents = calendar.components(units, from: startDate)
    startDateComponents.calendar = calendar as Calendar
    
    var endDateComponents = calendar.components(units, from: endDate as Date)
    endDateComponents.calendar = calendar as Calendar
    
    // Create the predicate for the query
    let summariesWithinRange =  HKQuery.predicate(forActivitySummariesBetweenStart: startDateComponents, end: endDateComponents)
    
    // Build the query
    let query = HKActivitySummaryQuery(predicate: summariesWithinRange) { (query, summaries, error) -> Void in
        guard let activitySummaries = summaries else {
            guard let queryError = error else {
                fatalError("*** Did not return a valid error object. ***")
            }
            
            // Handle the error here...
            return
        }
        
        for thisSummary in activitySummaries {
            //                print("for each summary")
            let standUnit = HKUnit.count()
            let standHours = thisSummary.appleStandHours.doubleValue(for: standUnit)
            //                let stoodThisHourMaybe = thisSummary.appleStandHours.categ //doubleValue(for: standUnit)
            //\(thisSummary.description) //stand unit _\(standUnit)_
            print("day#\(thisSummary.dateComponents(for: calendar as Calendar).day) stand hours \(standHours)  ")
        }//end for
        
        // Do something with the summaries here...
        
        for thisItem in activitySummaries {
            //thisItem.appleStandHours
            
            print("abc \( thisItem.appleStandHours ) " )
        }//end for
    }
    
    // Run the query
    let hkStore : HKHealthStore = HKHealthStore()
    hkStore.execute(query)
    
    //***
    let aStandHour =  HKCategoryType.categoryType(forIdentifier: .appleStandHour)
   
    // This is the type you want updates on. It can be any health kit type, including heart rate.
    //            let distanceType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.distanceWalkingRunning)
    
    // Match samples with a start date after the workout start
    //        let predicate =  .predicat //( , endDate: nil, options: .None)
    //        let theDate : Date =
    let thepredicate =  HKQuery.predicateForCategorySamples(with: .greaterThanOrEqualTo, value: 0) //.predicateForSamplesWithStartDate(startDate , endDate: nil, options: .None)
    //        predicate
    
    //        let predicate = .  //(theDate , endDate: nil, options: .None)
    let hka : HKQueryAnchor = HKQueryAnchor(fromValue: 0)
    let sHourQuery = HKAnchoredObjectQuery(type: aStandHour!, predicate: thepredicate, anchor: hka, limit: 0, resultsHandler: { ( query, samples, deletedObjects, anchor, error) -> Void in
        // Handle when the query first returns results
        // TODO: do whatever you want with samples (note you are not on the main thread)
        print("getting here A?")
        //            for thisSample in samples! {
        //                print("A smpLType \(thisSample.sampleType) thisSample \(thisSample)")
        //            }
    })
    
    // This is called each time a new value is entered into HealthKit (samples may be batched together for efficiency)
    
    sHourQuery.updateHandler = { (query, samples, deletedObjects, anchor, error) -> Void in
        // Handle update notifications after the query has initially run
        // TODO: do whatever you want with samples (note you are not on the main thread)
        print("getting here B?")
        for thisSample in samples! {
            print("B smpLType \(thisSample.sampleType) thisSample \(thisSample)")
        }
    }
    
    // Start the query
    self.hkStoreOnVC.execute(sHourQuery)
    //***
    
}//end func

func myCompletionHandler(bTest: Bool ) {
    print("my completion handler")
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

}//end viewController Class

这是代码输出 - 日志从未打印到“getting here b?”:
health kit authorize?
health kit test.
authorize health kit
health kit authorized
thisValue: HKQuantityTypeIdentifierStepCount
 authorize HK - steps count {(
    HKQuantityTypeIdentifierStepCount
)} 
2017-11-04 19:18:30.100562-0500 watchapptest[25048:4695625] refreshPreferences: HangTracerEnabled: 0
2017-11-04 19:18:30.100600-0500 watchapptest[25048:4695625] refreshPreferences: HangTracerDuration: 500
2017-11-04 19:18:30.100615-0500 watchapptest[25048:4695625] refreshPreferences: ActivationLoggingEnabled: 0 ActivationLoggingTaskedOffByDA:0
getting here A?
day#Optional(28) stand hours 14.0  
day#Optional(29) stand hours 14.0  
day#Optional(30) stand hours 14.0  
day#Optional(31) stand hours 14.0  
day#Optional(1) stand hours 16.0  
day#Optional(2) stand hours 13.0  
day#Optional(3) stand hours 15.0  
day#Optional(4) stand hours 13.0  
abc 14 count 
abc 14 count 
abc 14 count 
abc 14 count 
abc 16 count 
abc 13 count 
abc 15 count 
abc 13 count 
2个回答

3

我对HealthKit还不熟悉,所以可能有更好的方法来完成这个任务。但是这种方法对我来说似乎有效。我检查实际站立的分钟数,并使用minutes > 0调用完成处理程序。

    private let store = HKHealthStore()

    func askPermission() {
        let standType = HKQuantityType.quantityType(forIdentifier: .appleStandTime)!
        store.requestAuthorization(toShare: [], read: [standType], completion: { (success, error) in
            self.didStandThisHour { (didStand) in
                print("Did stand this hour: \(didStand)")
            }
        })
    }

    func didStandThisHour(_ didStand: @escaping (Bool) -> ()) {
        let store = HKHealthStore()
        let calendar = Calendar.autoupdatingCurrent
        let dateComponents = calendar.dateComponents([.year, .month, .day, .hour], from: Date())
        let endDate = Date()
        let startDate = calendar.date(from: dateComponents)!
        let standTime = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.appleStandTime)!
        var interval = DateComponents()
        interval.hour = 1
        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
        let query = HKStatisticsCollectionQuery(quantityType: standTime, quantitySamplePredicate: predicate, options: [.cumulativeSum], anchorDate: startDate, intervalComponents:interval)
        query.initialResultsHandler = { query, results, error in
            guard error == nil, let myResults = results else {
                fatalError("Something is wrong with HealthKit link")
            }
            myResults.enumerateStatistics(from: startDate, to: endDate, with: { (statistics, stop) in
                guard let quantity = statistics.sumQuantity() else {
                    didStand(false)
                    return
                }
                let minutes = quantity.doubleValue(for: .minute())
                didStand(minutes > 0)
            })
        }
        store.execute(query)
    }

1
我发现这种方法并不是100%可靠的。有时候,即使在活动中已经勾选了站立小时数,分钟数仍然为零。我不知道这是否与HealthKit同步有关。也许添加站立分钟到HealthKit会有一点延迟。 - dacvrijma
谢谢。当手表再次更新以显示您是否站立了这个小时时,我就停止关注它了。在一两个小版本中,苹果手表没有显示您当前的站立目标是否达成。这就是为什么我对构建一个手表应用程序来重新显示此指示器感兴趣的原因。 - Neo42
为什么不使用秒而非分钟?也许这就是为什么它会出现假阴性的原因。 - Radu Vlad

2

如果您想获取今天的活动环信息(包括站立小时数),您需要首先请求用户授权以检索所需的对象类型:

let healthStore = HKHealthStore()

let objectTypes: Set<HKObjectType> = [
    HKObjectType.activitySummaryType()
]

healthStore.requestAuthorization(toShare: nil, read: objectTypes) { (success, error) in

    // Authorization request finished, hopefully the user allowed access!
}

您可以使用此谓词来检索今天的日期:

let calendar = Calendar.autoupdatingCurrent

var dateComponents = calendar.dateComponents(
    [ .year, .month, .day ],
    from: Date()
)

// This line is required to make the whole thing work
dateComponents.calendar = calendar

let predicate = HKQuery.predicateForActivitySummary(with: dateComponents)

创建查询…
let query = HKActivitySummaryQuery(predicate: predicate) { (query, summaries, error) in

    guard let summaries = summaries, summaries.count > 0
    else {
        return
    }

    // Handle data
}

您将收到的数据类型为HKActivitySummary,您可以检索以下内容,例如:
let sandUnit = HKUnit.count()
let standHours = summary.appleStandHours.doubleValue(for: standUnit)

1
我需要表示他们是否在当前小时内停留的值。 - Neo42
你的回答不完整。目前的标准时间是难点,没有人在线上给出如何获取它的示例。 - Neo42
1
你可以在dateComponents变量中指定,尝试获取当前小时数。编辑:https://dev59.com/X3A85IYBdhLWcg3wJf4Z - Bruno Fulber Wide
请重新阅读我的问题,因为您仍然没有回答我的主要问题。我也会尝试进一步解释。Apple Watch存储两个不同的值。 StandHour表示这个小时是否站立。而它还存储了StandHours,即我今天站立了多少小时。 - Neo42
有人解决了这个问题吗?即:获取当前小时的AppleStandHour? - Gerard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接