Skip to content

Apple Health import: write steps to DailyMetric — iOS + Android (#89)#124

Merged
ryanbr merged 1 commit into
mainfrom
applehealth-steps-dailymetric-89
Jul 9, 2026
Merged

Apple Health import: write steps to DailyMetric — iOS + Android (#89)#124
ryanbr merged 1 commit into
mainfrom
applehealth-steps-dailymetric-89

Conversation

@ryanbr

@ryanbr ryanbr commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Fixes #89 (Apple Health steps not counted) on both platforms. Supersedes #97.

Root cause

Apple Health steps never reached DailyMetric.steps, so they never surfaced in the sourced-daily step arbitration:

  • Android: FusionDayAdapterWhoopRepository.dailyColumn("steps") = d.steps
  • iOS: metricValue = d.steps (Repository.swift:1792)

The importer wrote every other Apple-derivable daily field into DailyMetric, but the steps column was silently omitted from the constructor on both platforms — so DailyMetric.steps was always null for Apple Health.

Fix

Set steps in the DailyMetric the importer builds:

  • Android (AppleHealthImporter.kt): add steps = d.steps?.let { Math.round(it).toInt() } to the construction, and || d.steps != null to the hasDailyMetric gate so a steps-only day (no HR/HRV/sleep) still creates the row.
  • iOS (AppleHealthImport.swift): add steps: d.steps.map { Int($0) } to the construction (no gate — it already maps every day).

Why this over #97

#97 added only the gate clause (|| d.steps != null) but the constructor still never set steps — so it created bare all-null DailyMetric rows and left DailyMetric.steps null. #89 stayed broken. The actual gap was the missing constructor field; this sets it (plus the gate, for steps-only days).

No double-count: Apple steps win by arbitration tier (intended), and the aggregator already takes max-per-source, never a cross-source sum (#589).

Verification

  • Android: :app:compileFullDebugKotlin passes; the parse-side step aggregation is already covered by AppleHealthImporterToleranceTest (stepsDoNotDoubleCountAcrossSources).
  • iOS: rides the next build (app target can't compile on WSL); the added field mirrors the 8 sibling fields already in the same constructor.
  • No new unit test: the row-building takes a private day-aggregate + a Room repo with no JVM test seam, so covering it would need a refactor larger than the one-line fix. Flagging that explicitly rather than hiding it.

Apple Health steps never reached DailyMetric.steps, so they didn't surface in the
sourced-daily step arbitration (Android FusionDayAdapter via dailyColumn("steps")
= d.steps; iOS via metricValue = d.steps). Every OTHER Apple-derivable daily field
was written to DailyMetric, but the steps column was silently omitted from the
construction on BOTH platforms.

Fix: set steps in the DailyMetric row the Apple Health importer builds.
 - Android: add "steps = d.steps?.let { Math.round(it).toInt() }" to the
   construction, AND add "|| d.steps != null" to the hasDailyMetric gate so a
   steps-only day (no HR/HRV/sleep) still creates the row.
 - iOS: add "steps: d.steps.map { Int($0) }" to the construction (no gate there --
   it already maps every day).

Reimplements #97's intent correctly: #97 only added the gate clause, but the
constructor still never set steps, so it created bare all-null rows and left
DailyMetric.steps null -> #89 stayed broken. This sets the field (the actual gap).

No double-count: Apple steps win by arbitration tier (as intended), and the
aggregator already takes MAX-per-source, never a cross-source sum (#589).

Verified: Android :app:compileFullDebugKotlin passes; the parse-side step
aggregation is already covered by AppleHealthImporterToleranceTest. iOS rides the
next build (app target can't compile on WSL); the added field mirrors the 8
sibling fields in the same constructor.
@ryanbr ryanbr merged commit 59087b0 into main Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Steps not being counted after the update

1 participant