Ben Dodson

Freelance iOS, Apple Watch, and Apple TV Developer

HealthKit Radar 22977320

Working with any code, it is inevitable that you’ll stumble across bugs. When this happens with iOS development, Apple ask developers to submit a “radar” so that they can track and hopefully fix any issues that arise. The issue is that the Apple Bug Reporter is locked down and non-searchable so you have no idea if anyone else has submitted the same issue. Apple supposedly collate all of the bugs and mark any issues as duplicates and then use the number submitted to gauge how important an issue is and prioritise it’s fix. This has led to sites like Open Radar that let developers copy and paste their bug reports so other developers can search and then simply submit new bugs as a duplicate.

In any case, today I found an interesting bug in HealthKit. iOS 9 added a new method named deleteObjectsOfType:predicate:withCompletion which is supposed to let you delete any objects you have written to HealthKit nice and easily. For example, I might have an app which constantly checks your step count and flights climbed throughout the day and so it will want to delete any steps and flights from 00.00 to 23.59 before it adds the updated count. Unfortunately, the method doesn’t seem to respect the type parameter and so it will delete everything that matches your query regardless of what HKObjectType it is.

For example, consider the following code:

let date = NSDate()

let predicate = HKQuery.predicateForSamplesWithStartDate(date.beginningOfDay(), endDate: NSDate().endOfDay(), options: .None)

let stepType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!
healthStore.deleteObjectsOfType(stepType, predicate: predicate, withCompletion: { (success, count, error) -> Void in
    NSLog("deleted \(count) step objects: \(error)")
let stepQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: 9800)
let stepSample = HKQuantitySample(type: stepType, quantity: stepQuantity, startDate: date, endDate: date)
healthStore.saveObject(stepSample) { (success, error) -> Void in

let flightsType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierFlightsClimbed)!
healthStore.deleteObjectsOfType(flightsType, predicate: predicate, withCompletion: { (success, count, error) -> Void in
    NSLog("deleted \(count) flight objects: \(error)")
let flightsQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: 15)
let flightsSample = HKQuantitySample(type: flightsType, quantity: flightsQuantity, startDate: date, endDate: date)
healthStore.saveObject(flightsSample) { (success, error) -> Void in

The expected result is that you’ll end up with an entry for Step Count (9800 steps) and an entry for Flights Climbed (15). However, you’ll only get the Flights Climbed entry as the line healthStore.deleteObjectsOfType(flightsType, predicate: predicate, withCompletion:... will delete all entries that match the predicate, regardless of the HKObjectType. In addition, if you look at the headers for this method within the HealthKit framework, they don’t add up with what is expected:

This suggests that this method was refactored at some point prior to release and has this rather serious bug. I’ve filed this as a radar (rdar://22977320) but in the mean time you’ll need to do a manual search for objects with HKSampleQuery and delete them with a separate call if you want to avoid this.

Update: I’ve put together a fix for this issue with a new HKHealthStore method, deleteSamplesOfType:predicate:withCompletion:

Update: Whilst iOS 9.1 hasn’t fixed this issue, my bug report has today (23rd Oct 2015) been marked as a duplicate of issue 23097982 so looks like they may be getting around to fixing it. iOS 9.2 perhaps?

HKHealthStore Extension, deleteSamplesOfType » « The Times They Are A-Changin'