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

时间:2014-10-15 06:21:55

标签: ios swift background-process health-kit

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

3 个答案:

答案 0 :(得分:38)

经过一整天的测试(iOS 9.2),我可以在以下所有申请状态中确认HealthKit后台投放工作

  • 背景(在后台和执行代码中),
  • 已暂停(在后台但未执行代码),
  • 已终止(强制被用户杀死或被系统清除)。
  

请记住:第1部分

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

很遗憾,文档中没有关于每种数据类型的最低频率的信息,但我对Fitness types的体验如下:

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

注意immediate并不意味着实时,而是“活动数据样本写入HealthKit数据库/商店后不久”。 / p>

  

请记住:第2部分

如果使用密码锁定设备,则会调用 none 您的后台投放观察员。这是故意的,因为隐私问题(阅读更多:https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/)。

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

  

示例代码

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

答案 1 :(得分:22)

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

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

  1. 您需要在应用的功能中设置背景模式。见下图:
  2. enter image description here

    1. 然后您需要在Required Background Modes中添加info.plist,如下图所示:
    2. enter image description here

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

        3.1。从Scheme工具栏菜单中,选择iOS模拟器或设备。

        3.2。从同一菜单中选择“编辑方案”。

        3.3。在左侧列中,选择“运行”。

        3.4。选择选项选项卡。

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

      2. enter image description here

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

        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

        我希望这对你有所帮助。

答案 2 :(得分:1)

在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];
}