が実行されていない場合、HealthKitバックグラウンド配信でアプリケーションを起動できますか?特に終了状態では?
丸一日のテスト(iOS 9.2)の後、HealthKit
バックグラウンド配信が次のすべてのアプリケーション状態で機能することを確認できます。
覚えておいてください:パート1
一部のHealthKit
データ型の最小更新頻度はHKUpdateFrequencyHourly
です。とは言っても、バックグラウンド配信をHKUpdateFrequencyImmediate
の頻度で設定したとしても、1時間おきほどの頻度で更新を取得することはできません。
残念ながら、データ型ごとの最小頻度に関する情報はドキュメントにありませんが、Fitness types
での私の経験は次のとおりです。
注:immediate
はリアルタイムを意味するのではなく、アクティビティデータサンプルがHealthKit
データベースに書き込まれた「すぐ後の時間」を意味します/お店。
覚えておいてください:パート2
デバイスがパスコードでロックされている場合、バックグラウンド配信オブザーバーのnoneが呼び出されます。これはプライバシーの問題のために意図的なものです(詳細: https://developer.Apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/ )。
つまり、ユーザーがデバイスをロック解除するとすぐに、HealthKit
バックグラウンド配信オブザーバーが呼び出されます(もちろん、最小頻度時間が経過した場合)。
サンプルコード:
Viktor Siglerの回答をご覧ください。ただし、HealthKit
バックグラウンド配信が機能するために必要でもなければ必要でもないので、彼の回答の最初から3つのステップすべてをスキップできます。
この回答は少し遅れていますが、これが人々がHKObserverQuery
をうまく使用する方法を理解するのに役立つことを願っています。
まず、HKObserverQuery
がバックグラウンドモードで正常に動作し、アプリがまったく閉じられていない場合。ただし、すべてが正常に機能するためには、まずいくつかのオプションを設定する必要があります。
Required Background Modes
にinfo.plist
を追加する必要があります。次のようにBackground Fetch
を設定する必要があります。
3.1。スキームのツールバーメニューから、iOSシミュレータまたはデバイスを選択します。
3.2。同じメニューから、「スキームの編集」を選択します。
3.3。左側の列で、「実行」を選択します。
3.4。 [オプション]タブを選択します。
3.5。 [Background Fetch]チェックボックスを選択して、[閉じる]をクリックします。
その後、次のコードを使用して、アプリがバックグラウンドまたは閉じているときに通知を受け取ることができます。
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
がアクティブになり、通知がアクティブになります。
これがお役に立てば幸いです。
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];
}