Skip to content
Back to blog
Whispcal

Plugging into Apple Health without losing my mind

apple-healthgoogle-fithealth-datareact-nativeintegration

In early January, I decided WhispCal needed to sync with Apple Health and Google Fit. Users were already logging their meals — why not pull in weight, steps, and other health data to give a fuller picture?

The feature sounded like a weekend task. It took the better part of a week.

Library roulette

On January 6th, I pushed "implement health connect" — referring to Google's Health Connect API, which is the Android equivalent of Apple's HealthKit. The same day: "redo onboarding from scratch" and "use another library - focus on ios for now."

Three commits. Three different approaches. In a single day.

The React Native ecosystem for health data is fragmented. There are libraries for Apple HealthKit, separate libraries for Google Health Connect, and a few that try to wrap both. Each has different levels of maintenance, different API designs, and different opinions about how permissions should work.

I tried one library, hit a wall with its permission model. Switched to another, found it didn't support the data types I needed. Finally landed on a combination that worked — but only after deciding to "focus on iOS for now" and treat Android as a follow-up.

The weight sync rabbit hole

Getting weight data from Apple Health seems simple: request permission, query for weight entries, display them. In practice, every step has edge cases.

Users might have weight data from a smart scale, from manual entries, from other apps, or from all three simultaneously. The same weight might appear multiple times if it was synced across devices. Some entries have timestamps accurate to the second; others just have a date.

January 9th brought "Fix weight sync on google fit" — a commit that hides hours of debugging why Google Fit returned different weight values than Apple Health for the same user on the same day. The answer, predictably, was timezone handling.

The Pixel 4 incident

One of the more creative bugs: Google Fit on older Android devices couldn't handle large health data queries. My Pixel 4 test device would hang when I requested more than 30 days of historical data.

The fix was pragmatic: "remove health data before 30 days to allow pixel 4." Not elegant, but functional. Sometimes the right engineering decision is to accept a limitation and move on.

Syncing as a background concern

The initial implementation synced health data when the user opened the app. This was fine for me during development — I opened the app dozens of times a day. But for real users who might open it once at dinner, the sync needed to happen automatically.

By March, I had "auto sync health every day" running as a background task. But getting background execution right on both platforms is its own adventure. iOS is notoriously restrictive about what apps can do in the background. Android is more permissive but less predictable about when background tasks actually run.

Don't show what isn't there

A small but important UX detail: "Don't show sync health data if device does not have ability." Not every Android device has Google Health Connect installed, and some older devices don't support it at all. Showing a "sync health" button that leads to an error is worse than not showing it.

This is the kind of feature that sounds like a single line item in a spec document but expands into days of platform-specific logic, library evaluation, and edge case handling. The git history makes it look clean — a few commits over a few days. The reality was a week of reading documentation, testing on multiple devices, and making pragmatic compromises.

Health data sync is live now, and users barely notice it. Which is exactly how infrastructure features should feel.