当应用程序未运行时的Healthkit后台传送

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

经过一整天的testing(iOS 9.2)后,我可以确认HealthKit后台传送在以下所有应用程序状态下都能正常工作

  • 背景 (在后台执行代码),
  • 暂停 (在后台但不执行代码),
  • 终止 (由用户强行杀死或由系统清除)。

请记住 :第1部分

一些HealthKit数据types的最低更新频率为HKUpdateFrequencyHourly 。 也就是说,即使您设置的后台传送频率为HKUpdateFrequencyImmediate ,也不会每隔一小时左右更新一次。

不幸的是,关于每个数据types的最小频率的文档没有任何信息,但是我对Fitness types经验如下:

  • 活跃能源: 每小时
  • 骑自行车距离: 直达
  • 航class攀登: 立即
  • NikeFuel: 立即
  • 步骤: 每小时
  • 步行+跑步距离: 每小时
  • 锻炼: 立即

注意immediate不意味着实时,而是将活动数据样本写入HealthKit数据库/商店之后不久。

请记住 :第2部分

如果设备被locking了密码,那么您的后台传送观察员都不会被调用。 这是有意的,由于隐私问题(阅读更多: https : //developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/ )。

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

示例代码

看看Viktor Sigler的回答。 虽然,您可以跳过答案开始的所有三个步骤,因为HealthKit后台投递不需要也不需要。

这个答案有些晚,但我希望这有助于人们了解如何成功地使用HKObserverQuery

首先HKObserverQuery在后台模式下工作正常,当应用程序closures 。 但是,您需要先设置一些选项,以允许一切工作正常。

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

在这里输入图像描述

  1. 然后,您需要在info.plist添加Required Background Modes ,如下图所示:

在这里输入图像描述

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

    3.1。 从Scheme工具栏菜单中,select一个iOS模拟器或设备。

    3.2。 从相同的菜单中select编辑scheme。

    3.3。 在左栏中,select运行。

    3.4。 select选项选项卡。

    3.5。 select背景提取checkbox,然后单击closures。

在这里输入图像描述

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

 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 } 

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

我希望这可以帮助你。

在iOS 8.1中呢。 您需要确保在应用程序委托的application:didFinishLaunchingWithOptions:重新创build观察者查询。 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]; } 
Interesting Posts