Фоновая доставка Healthkit, когда приложение не запущено
Может ли фоновая доставка HealthKit запустить приложение, если оно не запущено? Особенно в расторгнутом состоянии?
3 ответа
После полного дня тестирования (iOS 9.2) я могу подтвердить, что HealthKit
Фоновая доставка РАБОТАЕТ во всех следующих состояниях приложения:
- фон (в фоновом режиме и выполнение кода),
- приостановлено (в фоновом режиме, но не выполняется код),
- прекращено (принудительно уничтожено пользователем или удалено системой).
Имейте в виду: часть 1
Немного HealthKit
Типы данных имеют минимальную частоту обновления HKUpdateFrequencyHourly
, Тем не менее, даже если вы настроили фоновую доставку с частотой HKUpdateFrequencyImmediate
вы не будете получать обновления чаще, чем каждый час или около того.
К сожалению, в документации нет информации о минимальных частотах для каждого типа данных, но мой опыт работы с Fitness types
было следующим:
- Активная энергия: почасовая,
- Велоспорт расстояние: немедленно,
- Перелеты: немедленные,
- NikeFuel: немедленно,
- Шаги: ежечасно,
- Ходьба + Бег Расстояние: почасово,
- Тренировки: немедленные.
Примечание: immediate
НЕ означает "в реальном времени", а скорее "через некоторое время после", когда образцы данных о деятельности были записаны в HealthKit
базы данных / магазин.
Имейте в виду: часть 2
Если устройство заблокировано паролем, никто из вас не будет вызывать вас в фоновом режиме. Это сделано намеренно из-за проблем с конфиденциальностью (подробнее: https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/).
Тем не менее, как только пользователь разблокирует устройство, ваш HealthKit
Будут вызваны наблюдатели в фоновом режиме (если, конечно, прошло минимальное время).
Пример кода:
Посмотрите на ответ Виктора Сиглера. Хотя, вы можете пропустить все три шага с начала его ответа, так как они не требуются и не нужны для HealthKit
Фоновая доставка на работу.
Этот ответ немного запоздал, но я надеюсь, что это поможет людям понять, как работать с HKObserverQuery
успешно.
Прежде всего HKObserverQuery
отлично работает в фоновом режиме и когда приложение вообще закрыто. Но сначала нужно установить некоторые параметры, чтобы все работало нормально.
- Вам необходимо установить фоновые режимы в возможностях вашего приложения. Смотрите ниже изображение:
- Затем вам нужно добавить
Required Background Modes
в вашемinfo.plist
как на следующей картинке:
Вам нужно установить
Background Fetch
следующим образом:3.1. В меню панели инструментов "Схема" выберите "Симулятор iOS" или "Устройство".
3.2. В том же меню выберите "Редактировать схему".
3.3. В левом столбце выберите "Выполнить".
3.4. Выберите вкладку "Параметры".
3.5. Установите флажок "Фоновая выборка" и нажмите "Закрыть".
Тогда вы можете получать уведомления, когда приложение находится в фоновом режиме или закрыто, используя следующий код:
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
}
В приведенном выше методе HKObserver
активируется, если авторизация HealthKit предоставлена пользователем, а затем активировать уведомления.
Я надеюсь, что это поможет вам.
В 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];
}