1

I'm trying to implement background delivery of the HealthKit data for an independent watchOS 8 app. I was following Gettings the most out of HealthKit WWDC talk and seems to have added everything that is needed for background delivery to work, including recent iOS 15 and watchOS 8 com.apple.developer.healthkit.background-delivery entitlement. But for some reason, background delivery stops working after approximately 3-5 hours after the app went to the background. For example, I'm receiving updates during the evening from the app, but then over the night updates stops delivering and I'm getting those only if I open the app again in the morning. See the ExtensionDelegate code below

class ExtensionDelegate: NSObject, WKExtensionDelegate {
    private let healthStore = HKHealthStore()
    private var anchor: HKQueryAnchor?

    func applicationDidFinishLaunching() {
        print("application did finish launching")

        activateHeathKit()
    }

    func activateHeathKit() {
        let types = Set([HKObjectType.categoryType(forIdentifier: .lowHeartRateEvent)!])

        healthStore.requestAuthorization(toShare: nil, read: types) { [weak self] success, _ in
            guard let `self` = self else {
                return
            }

            guard let lowHeartRateType = HKObjectType.categoryType(forIdentifier: .lowHeartRateEvent) else {
                return
            }

            `self`.healthStore.enableBackgroundDelivery(for: lowHeartRateType, frequency: .immediate) { success, _ in
                print("enableBackgroundDelivery: \(success) for lowHeartRateEvent")
            }

            let query = HKObserverQuery(sampleType: stepsType, predicate: nil) { _, completionHandler, error in
                `self`.updateLowHeartRate {
                    completionHandler()
                }
            }
            `self`.healthStore.execute(query)
        }
    }

    func updateLowHeartRate(completionHandler: @escaping () -> Void) {
        guard let lowHeartRateType = HKObjectType.categoryType(forIdentifier: .lowHeartRateEvent) else {return}

        let anchoredQuery = HKAnchoredObjectQuery(type: lowHeartRateType, predicate: nil, anchor:
                                                    self.anchor, limit: Int(HKObjectQueryNoLimit)) { [unowned self] query, newSamples,
            _, newAnchor, error -> Void in

            for item in newSamples ?? [] {
                let date = item.startDate
                let hour = Calendar.current.component(.hour, from: date)
                let minute = Calendar.current.component(.minute, from: date)

                let message = "Low heart rate from \(hour):\(String(format: "%02d", minute))"
                print(message)
            }

            self.anchor = newAnchor

            completionHandler()
        }

        healthStore.execute(anchoredQuery)
    }    
}
Sanya
  • 159
  • 8

1 Answers1

1

I don't see an implementation of the handle(_:) method for background tasks but perhaps it is just not shown. Link to the docs here.

Just in case here is how I have my workout app set up to update complications on the watch face.

func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
    for task in backgroundTasks {
        if WKExtension.shared().applicationState == .background {
            if let watchComplication = task as? WKApplicationRefreshBackgroundTask {
                // do background work here
            }
        }
        task.setTaskCompletedWithSnapshot(false)
    }
    completePendingTasksIfNeeded()
}
Dan O'Leary
  • 916
  • 9
  • 20