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

La livraison en arrière-plan HealthKit peut-elle lancer l'application si elle ne fonctionne pas? Particulièrement dans un état terminé?

Après une journée complète de test (iOS 9.2), je peux confirmer que la livraison de fond HealthKit dans tous les états d'application suivants:

  • arrière-plan (en arrière-plan et code d'exécution),
  • suspendu (en arrière-plan mais sans code d'exécution),
  • terminée (forcée par l'user ou purgée 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 livraison en arrière-plan avec la fréquence HKUpdateFrequencyImmediate , vous n'obtiendrez pas de mises à jour plus souvent que toutes les heures.

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

  • Energie active: horaire ,
  • Distance à vélo: immédiate ,
  • Vols Grimpés: immédiat ,
  • NikeFuel: immédiat ,
  • Étapes: toutes les heures ,
  • Marcher + Course à pied: toutes les heures ,
  • Entraînements: immédiat .

Remarque : immediate NE signifie PAS en time réel mais plutôt "quelque time après" les échantillons de données d'activité ont été écrits dans la database / magasin HealthKit .

Gardez à l'esprit : partie 2

Si le périphérique est verrouillé avec un code d'access, aucun de vos observateurs de livraison en arrière-plan ne 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'user déverrouille l'appareil, vos observateurs de la livraison de fond HealthKit seront appelés (si le time minimum de fréquence est passé, bien sûr).

Exemple de code :

Jetez un oeil à la réponse de Viktor Sigler. Bien que, vous pouvez passer les trois étapes à partir du début de sa réponse, car ils ne sont pas nécessaires ni nécessaires pour la livraison de fond HealthKit au travail.

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, HKObserverQuery fonctionne HKObserverQuery en arrière-plan et lorsque l'application est fermée . Mais vous devez d'abord définir certaines options pour que tout fonctionne correctement.

  1. Vous devez définir les modes d'arrièreplan dans les fonctionnalités de votre application. Voir l'image ci-dessous:

entrez la description de l'image ici

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

entrez la description de l'image ici

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

    3.1. Dans le menu de la barre d'outils Schéma, choisissez un simulateur ou un périphérique iOS.

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

    3.3. Dans la colonne de gauche, select Exécuter.

    3.4. Sélectionnez l'onglet Options.

    3.5. Sélectionnez la checkbox Arrière-plan et click Fermer.

entrez la description de l'image ici

Vous pouvez ensuite 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, HKObserver est activé si l'autorisation HealthKit est accordée par l'user, puis active les notifications.

J'espère que cela t'aidera.

Dans iOS 8.1, c'est le cas. Vous devez vous assurer de recréer vos requêtes d'observateur dans l'application de votre délégué d' application:didFinishLaunchingWithOptions: cependant. Un bogue dans 8.0 empêche la notification d'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]; }