web-dev-qa-db-fra.com

Livraison en arrière-plan de Healthkit lorsque l'application n'est pas en cours d'exécution

La livraison en arrière-plan de HealthKit peut-elle lancer l'application si elle n'est pas en cours d'exécution? Particulièrement dans un état terminé?

27
Ujjwal

Après une journée complète de tests (iOS 9.2), je peux confirmer que HealthKit la livraison en arrière-plan FONCTIONNE dans tous les états d'application suivants:

  • arrière-plan (en arrière-plan et exécution de code),
  • suspendu (en arrière-plan mais sans exécuter de code),
  • terminé (tué de force par l'utilisateur ou purgé par le système).

Gardez à l'esprit : partie 1

Certains types de données HealthKit ont une fréquence de mise à jour minimale de HKUpdateFrequencyHourly. Cela dit, même si vous configurez une diffusion en arrière-plan avec la fréquence HKUpdateFrequencyImmediate, vous ne recevrez pas les mises à jour plus souvent que toutes les heures environ.

Malheureusement, il n'y a pas d'informations dans la documentation sur les fréquences minimales par type de données, mais mon expérience avec Fitness types était le suivant:

  • Énergie active: toutes les heures ,
  • Distance à vélo: immédiate ,
  • Vols montés: immédiat ,
  • NikeFuel: immédiat ,
  • Étapes: toutes les heures ,
  • Distance marche + course: toutes les heures ,
  • Entraînements: immédiat .

Remarque: immediate NE SIGNIFIE PAS en temps réel mais plutôt "peu de temps après" que les échantillons de données d'activité ont été écrits dans la base de données/magasin HealthKit.

Gardez à l'esprit : partie 2

Si l'appareil est verrouillé avec un mot de passe, aucun de vous observateurs de livraison en arrière-plan sera appelé. Ceci est intentionnel en raison des problèmes de confidentialité (en savoir plus: https://developer.Apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/ ).

Cela dit, dès que l'utilisateur déverrouille l'appareil, vos observateurs de livraison en arrière-plan HealthKit seront appelés (si le temps de fréquence minimum est passé, bien sûr).

Exemple de code :

Jetez un œil à la réponse de Viktor Sigler. Cependant, vous pouvez ignorer les trois étapes depuis le début de sa réponse car elles ne sont ni requises ni nécessaires pour que la livraison en arrière-plan HealthKit fonctionne.

42
damirstuhec

Cette réponse est tardive mais j'espère que cela aidera les gens à comprendre comment travailler avec HKObserverQuery avec succès.

Tout d'abord, le HKObserverQuery fonctionne bien en arrière-plan et lorsque l'application est fermée du tout. Mais vous devez d'abord définir certaines options pour que tout fonctionne correctement.

  1. Vous devez définir les Modes d'arrière-plan dans les capacités de votre application. Voir l'image ci-dessous:

enter image description here

  1. Ensuite, vous devez ajouter le Required Background Modes dans votre info.plist comme dans l'image suivante:

enter image description here

  1. Vous devez définir le Background Fetch de la manière suivante:

    3.1. Dans le menu de la barre d'outils Scheme, choisissez un simulateur ou un appareil iOS.

    3.2. Dans le même menu, choisissez Modifier le schéma.

    3.3. Dans la colonne de gauche, sélectionnez Exécuter.

    3.4. Sélectionnez l'onglet Options.

    3.5. Cochez la case Récupération en arrière-plan et cliquez sur Fermer.

enter image description here

Ensuite, vous pouvez recevoir des notifications lorsque l'application est en arrière-plan ou fermée à l'aide du code suivant:

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   

}

Dans la méthode ci-dessus, le HKObserver est activé si l'autorisation HealthKit est accordée par l'utilisateur, puis active les notifications.

J'espère que cela vous aidera.

23
Victor Sigler

Dans iOS 8.1, c'est le cas. Vous devez vous assurer de recréer vos requêtes d'observateur dans le délégué de votre application application:didFinishLaunchingWithOptions:, bien que. Un bogue dans 8.0 empêche la notification en arrière-plan de HealthKit de fonctionner.

MODIFIER:

Dans votre 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