应用程序未运行时的Healthkit后台交付

Ujj*_*wal 25 background-process ios swift healthkit

如果没有运行,HealthKit后台交付可以启动应用程序吗?特别是在终止状态?

dam*_*hec 43

测试的一整天(的iOS 9.2)后,我可以证实,HealthKit后台交付不工作在以下所有应用程序的状态:

  • 背景(在后台和执行代码),
  • 暂停(在后台但没有执行代码),
  • 终止(由用户强制终止或由系统清除).

请记住:第1部分

某些HealthKit数据类型的最低更新频率为HKUpdateFrequencyHourly.也就是说,即使您设置了频率的后台交付HKUpdateFrequencyImmediate,您也不会比每小时左右更频繁地获得更新.

不幸的是,关于每种数据类型的最低频率的文档中没有信息,但我的经验Fitness types如下:

  • 活跃能源:每小时,
  • 骑行距离:立即,
  • 航班爬升:立即,
  • 耐克燃料:立即,
  • 步骤:每小时,
  • 步行+跑步距离:每小时,
  • 锻炼:立即.

注意:immediate并不意味着实时,而是"活动数据样本写入HealthKit数据库/商店后不久" .

请记住:第2部分

如果使用密码锁定设备,则不会调用任何后台传递观察器.这是故意的,因为隐私问题(更多信息:https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/).

也就是说,只要用户解锁设备,您的  HealthKit后台投递观察员就会被调用(如果最小频率时间已经过去).

示例代码:

看看Viktor Sigler的答案.虽然,您可以从答案开始时跳过所有三个步骤,因为它们不是必需的,也不需要HealthKit后台交付才能工作.

  • 为了在应用程序终止时支持后台传递,应该在 AppDelegate 的“didFinishLaunchingWithOptions:”中设置观察者。如果出现您正在观察的类型的新数据,HealthKit 将在您的应用程序实际向相关观察者发送通知之前**启动您的应用程序。通过在“didFinishLaunchingWithOptions:”中设置观察者查询,您将在观察者准备就绪时收到有关任何新数据的通知。https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HKObserverQuery_Class/index.html#//apple_ref/doc/uid/TP40014747 (2认同)

Vic*_*ler 23

这个答案有些迟,但我希望这有助于人们了解如何HKObserverQuery成功地工作.

首先,HKObserverQuery在后台模式和应用程序完全关闭时工作正常.但是你需要先设置一些选项才能让一切正常.

  1. 您需要在应用程序的功能中设置背景模式.见下图:

在此输入图像描述

  1. 然后你需要添加Required Background Modes你的info.plistas如下图所示:

在此输入图像描述

  1. 您需要Background Fetch按以下方式设置:

    3.1.从"方案"工具栏菜单中,选择iOS模拟器或设备.

    3.2.从同一菜单中,选择"编辑方案".

    3.3.在左侧列中,选择"运行".

    3.4.选择"选项"选项卡.

    3.5.选择Background Fetch复选框,然后单击Close.

在此输入图像描述

然后,您可以在应用处于后台或使用以下代码关闭时收到通知:

import UIKit
import HealthKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

   var window: UIWindow?

   let healthKitStore:HKHealthStore = HKHealthStore()

   func startObservingHeightChanges() {

       let sampleType =  HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)

       var query: HKObserverQuery = HKObserverQuery(sampleType: sampleType, predicate: nil, updateHandler: self.heightChangedHandler)

       healthKitStore.executeQuery(query)
       healthKitStore.enableBackgroundDeliveryForType(sampleType, frequency: .Immediate, withCompletion: {(succeeded: Bool, error: NSError!) in

           if succeeded{
               println("Enabled background delivery of weight changes")
           } else {
               if let theError = error{
                   print("Failed to enable background delivery of weight changes. ")
                   println("Error = \(theError)")
               }
           }
       })
   }


   func heightChangedHandler(query: HKObserverQuery!, completionHandler: HKObserverQueryCompletionHandler!, error: NSError!) {        

       // Here you need to call a function to query the height change

       // Send the notification to the user
       var notification = UILocalNotification()
       notification.alertBody = "Changed height in Health App"
       notification.alertAction = "open"        
       notification.soundName = UILocalNotificationDefaultSoundName   

       UIApplication.sharedApplication().scheduleLocalNotification(notification)

       completionHandler()
   }

   func authorizeHealthKit(completion: ((success:Bool, error:NSError!) -> Void)!) {

       // 1. Set the types you want to read from HK Store
       let healthKitTypesToRead = [
        HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth),
        HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBloodType),
        HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex),
        HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass),
        HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight),
        HKObjectType.workoutType()
       ]

       // 2. Set the types you want to write to HK Store
       let healthKitTypesToWrite = [
        HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex),
        HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned),
        HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning),
        HKQuantityType.workoutType()
       ]

       // 3. If the store is not available (for instance, iPad) return an error and don't go on.
       if !HKHealthStore.isHealthDataAvailable() {
           let error = NSError(domain: "any.domain.com", code: 2, userInfo: [NSLocalizedDescriptionKey:"HealthKit is not available in this Device"])

           if( completion != nil ) {                
               completion(success:false, error:error)
           }
           return;
       }

       // 4.  Request HealthKit authorization
       healthKitStore.requestAuthorizationToShareTypes(Set(healthKitTypesToWrite), readTypes: Set(healthKitTypesToRead)) { (success, error) -> Void in
           if( completion != nil ) {

               dispatch_async(dispatch_get_main_queue(), self.startObservingHeightChanges)
               completion(success:success,error:error)
           }
       }
   }   

   func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

       application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: .Alert | .Badge | .Sound, categories: nil))

       self.authorizeHealthKit { (authorized,  error) -> Void in
           if authorized {
               println("HealthKit authorization received.")
           }
           else {
               println("HealthKit authorization denied!")
               if error != nil {
                   println("\(error)")
               }
           }
       }

       return true
   }      


   //Rest of the defaults methods of AppDelegate.swift   

}
Run Code Online (Sandbox Code Playgroud)

在上述方法中,HKObserver如果用户授予HealthKit授权,则激活,然后激活通知.

我希望这对你有帮助.


drd*_*ger 3

在 iOS 8.1 中确实如此。不过,您需要确保在应用程序委托中重新创建观察者查询application:didFinishLaunchingWithOptions:。8.0 中的一个错误导致 HealthKit 的后台通知根本无法工作。

编辑:

在您的AppDelegate中:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //create/get your HKHealthStore instance (called healthStore here)
    //get permission to read the data types you need.
    //define type, frequency, and predicate (called type, frequency, and predicate here, appropriately)

    UIBackgroundTaskIdentifier __block taskID = [application beginBackgroundTaskWithExpirationHandler:^{
        if (taskID != UIBackgroundTaskInvalid) {
            [application endBackgroundTask:taskID];
            taskID = UIBackgroundTaskInvalid;
        }
    }];
    [healthStore enableBackgroundDeliveryForType:type frequency:frequency withCompletion:^(BOOL success, NSError *error) {}];
    HKQuery *query = [[HKObserverQuery alloc] initWithSampleType:healthType predicate:predicate updateHandler:
        ^void(HKObserverQuery *query, HKObserverQueryCompletionHandler completionHandler, NSError *error)
        {
            //If we don't call the completion handler right away, Apple gets mad. They'll try sending us the same notification here 3 times on a back-off algorithm.  The preferred method is we just call the completion handler.  Makes me wonder why they even HAVE a completionHandler if we're expected to just call it right away...
            if (completionHandler) {
                completionHandler();
            }
            //HANDLE DATA HERE
            if (taskID != UIBackgroundTaskInvalid) {
                [application endBackgroundTask:taskID];
                taskID = UIBackgroundTaskInvalid;
            }
        }];
    [healthStore executeQuery:query];
}
Run Code Online (Sandbox Code Playgroud)

  • 到目前为止,我还没有在 iOS 8.1 上看到有效的代码示例。您能推荐一款可以让您对后台更新做出反应的产品吗? (2认同)
  • 您是否测试过代码以从 watchOS2 锻炼和 IOS9.1 实时接收心率数据?HKObserverQuery 仅在启动时触发,或者 IOS 应用程序从后台转到前台时触发?是BUG吗? (2认同)