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é?
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:
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:
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.
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.
Required Background Modes
dans votre info.plist
comme dans l'image suivante: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.
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.
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];
}