DEV Community

Philip D'Souza
Philip D'Souza

Posted on

Why your Apple Health export has silent gaps

An Apple Health export can look complete and still be missing days. Here is the HealthKit detail behind the hole, and the three calls that show it in your own file.

Every HealthKit sample carries a start date and an end date. When it was added to the store is a separate thing, and Apple does not expose it as a property you can filter a predicate by. That is the whole bug. A wearable that syncs a week late, a restore, a new phone: in each case the sample's date is old and its arrival is new, so a cursor keyed on dates moves past it permanently.

The fix is not a smarter date filter. HKAnchoredObjectQuery starts from a nil anchor and a persistent one after that; its documentation is explicit that it returns samples saved after the anchor, not samples inside a date range, and it reports deletions too. The other correct design abandons the cursor and re-reads a bounded trailing window every run.

The part worth copying is the gap detector. get_mcp_status reports lastDataDate and metricCount, get_health_metrics returns a coverage block with firstDate, lastDate and days, and get_trends returns windowSatisfied, which goes false when your file does not hold enough history for the window you asked for. An earlier revision relaxed that check, and a 365-day request against 400 days of history reported satisfied while comparing 365 days to 35. A gap detector that can miss a gap is worse than none.

Full post: https://www.healthexport.dev/blog/apple-health-export-silent-gaps?utm_source=devto&utm_medium=social&utm_campaign=exp-20261001-hea169-silentgaps

Top comments (1)

Collapse
 
marcusykim profile image
Marcus Kim •

The late-arriving sample is a great regression fixture: complete an export, insert a sample with an older measurement date, then export again and verify it appears exactly once. I would put the 365-versus-35 comparison beside that test so a complete-looking coverage flag cannot hide unequal comparison windows. Do you expose the actual days used on each side?