web-dev-qa-db-ja.com

アプリが実行されていないときのHealthkitバックグラウンド配信

が実行されていない場合、HealthKitバックグラウンド配信でアプリケーションを起動できますか?特に終了状態では?

27
Ujjwal

丸一日のテスト(iOS 9.2)の後、HealthKitバックグラウンド配信が次のすべてのアプリケーション状態で機能することを確認できます

  • background(バックグラウンドで実行中のコード)、
  • suspended(バックグラウンドで実行されていないコード)、
  • terminated(ユーザーによって強制終了されたか、システムによって削除されました)。

覚えておいてください:パート1

一部のHealthKitデータ型の最小更新頻度はHKUpdateFrequencyHourlyです。とは言っても、バックグラウンド配信をHKUpdateFrequencyImmediateの頻度で設定したとしても、1時間おきほどの頻度で更新を取得することはできません。

残念ながら、データ型ごとの最小頻度に関する情報はドキュメントにありませんが、Fitness typesでの私の経験は次のとおりです。

  • 有効エネルギー:毎時
  • サイクリング距離:immediate
  • 上昇したフライト:即時
  • NikeFuel:即時
  • 手順:毎時
  • 徒歩+走行距離:毎時
  • ワークアウト:immediate

immediateはリアルタイムを意味するのではなく、アクティビティデータサンプルがHealthKitデータベースに書き込まれた「すぐ後の時間」を意味します/お店。

覚えておいてください:パート2

デバイスがパスコードでロックされている場合、バックグラウンド配信オブザーバーのnoneが呼び出されます。これはプライバシーの問題のために意図的なものです(詳細: https://developer.Apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/ )。

つまり、ユーザーがデバイスをロック解除するとすぐに、HealthKitバックグラウンド配信オブザーバーが呼び出されます(もちろん、最小頻度時間が経過した場合)。

サンプルコード

Viktor Siglerの回答をご覧ください。ただし、HealthKitバックグラウンド配信が機能するために必要でもなければ必要でもないので、彼の回答の最初から3つのステップすべてをスキップできます。

42
damirstuhec

この回答は少し遅れていますが、これが人々がHKObserverQueryをうまく使用する方法を理解するのに役立つことを願っています。

まず、HKObserverQueryがバックグラウンドモードで正常に動作し、アプリがまったく閉じられていない場合。ただし、すべてが正常に機能するためには、まずいくつかのオプションを設定する必要があります。

  1. アプリのCapabilitiesBackground Modesを設定する必要があります。下の画像を参照してください:

enter image description here

  1. 次に、次の図のようにRequired Background Modesinfo.plistを追加する必要があります。

enter image description here

  1. 次のようにBackground Fetchを設定する必要があります。

    3.1。スキームのツールバーメニューから、iOSシミュレータまたはデバイスを選択します。

    3.2。同じメニューから、「スキームの編集」を選択します。

    3.3。左側の列で、「実行」を選択します。

    3.4。 [オプション]タブを選択します。

    3.5。 [Background Fetch]チェックボックスを選択して、[閉じる]をクリックします。

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がアクティブになり、通知がアクティブになります。

これがお役に立てば幸いです。

23
Victor Sigler

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];
}
1
drdaanger