Writing

When the blocker becomes the package

Nov 26, 20252 min readOpen SourceReact Native

Back in March, when I shipped my first package, I made myself a promise: every time I solve a native-bridge problem twice, it becomes a package. This is the second installment — and this time the bridge problem was blocking my own startup.

The blocker

I'm building Kalma — a place where your health data actually belongs to you, especially if your medical history is scattered across countries and providers the way it is for nomads and expats. For that to work, Kalma has to read the data your phone already collects: steps, sleep, heart rate, workouts.

On iOS that means HealthKit. On Android it now means Health Connect. And that "now" is the problem — most of the libraries I evaluated were built for a world before Health Connect, wrapped only one platform, or hadn't kept pace with the New Architecture. I kept hitting walls, and the feature kept slipping.

So I did the thing I said I'd do: built the bridge properly, once, in the open.

One API, both platforms

pnpm add @mbdayo/react-native-health-kits
import HealthKits from "@mbdayo/react-native-health-kits";

if (await HealthKits.isAvailable()) {
  await HealthKits.requestPermissions(["steps", "sleep", "heartRate"]);
  const steps = await HealthKits.readData("steps", { aggregate: true });
}

Under the hood it's Turbo Modules wrapping HealthKit on iOS and Health Connect on Android, with one surface for both: reading, writing, real-time subscriptions, and 25+ data types — from heart rate and sleep to nutrition, blood pressure, and hydration.

What two platforms teach you about one idea

The interesting part wasn't the bridging; it was the philosophy gap. iOS and Android have fundamentally different opinions about the same concept:

  • Permissions: HealthKit wants usage strings in Info.plist and asks per data type; Health Connect wants manifest entries and a registered permission activity. Same idea, two ceremonies.
  • Availability: HealthKit needs a physical device; Health Connect is built into Android 14 but a separate install on Android 9–13. isAvailable() exists because "it depends" needed to be one call.
  • Aggregation: the platforms only agree on summing a handful of types — steps, distance, calories, floors, hydration. Pretend otherwise in the API and you'd be lying to someone. The README says so out loud instead.

Designing the unified surface meant choosing honesty over symmetry wherever the platforms disagree. That's the real work of a bridge library — the native calls are the easy part.

The streak continues

Kalma's health-data feature shipped on this package, which is the only benchmark that matters: it's extracted from production need, not imagined API design. Two packages into the @mbdayo scope, the promise is holding.

If you're pulling health data into a React Native app, give it a try — and if something's off, you know where the issues tab is.