-
-
Today: your day on one strand, a plain-language read, and Recovery, Effort and Rhythm. Next to it, the sleep page.
-
The paywall. Switch the engine to Cloud and the fire turns violet.
-
The day calendar, and the resting heart rate page with its cited usual range.
-
Health Recaps: your year told in one valley that changes from page to page.
-
Recap story cover (frameless, 1179 x 2556).
-
App icon (1024 x 1024).
-
The paywall on Cloud (frameless, 1179 x 2556).
-
Today (frameless, 1179 x 2556).
Inspiration
I'd been opening the Apple Health app for years and getting almost nothing out of it. Rings, charts, numbers stacked on numbers, none of it tells me what it actually means.
That's the actual problem with health apps. Measuring is solved. Your watch, your ring, your phone, they all count everything perfectly and then hand you a wall of numbers and quietly make it your job to figure out what they mean. Apple Health is a beautiful filing cabinet that explains nothing.
I wanted the thing that reads the data and just tells you what happened to your body last night in plain words.
What it does
Simple Health reads what's already in Apple Health and explains it to you.
- A read on your day. Every morning it writes a few sentences about you: what your sleep did to your recovery, what shifted, what's worth noticing. Not a dashboard. A paragraph.
- Three numbers for the day. Recovery, Effort and Rhythm, worked out on your phone from your own history, not somebody else's average. A small arrow on every row shows which way it moved since yesterday, green when that's good for you, red when it isn't.
- Fitness age. Your VO2 max placed against published norms for your age and sex, with the sources right there on the page.
- Your whole day on one strand. Wake to sleep, with every moment where it actually happened: a night's sleep, a workout, a long walk, time in daylight, a heart rate spike. Tap any of them to see the actual readings, with a source for anything that makes a health claim.
- A calendar of your days. Tap the date and every day turns into a little pile of what you did.
- Chat that's actually read your history. It has its own tab. Ask "how's my sleep been this year" and it answers from your own multi-year record, not a WebMD article.
- A year in review you can share, built from your own data.
- Widgets and an Apple Watch app, so the read is one glance away.
- Your Today, your order. Drag the rows around, hide the ones you don't care about.
- English, Spanish and German, and the AI answers in your language too.
It's source-agnostic on purpose. Whatever writes to Apple Health, an Apple Watch, an Oura ring, a Whoop, a smart scale, some random app, lands on your timeline. You don't need an Apple Watch. Nothing to log, no forms.
How I built it
The app. SwiftUI, running on iOS 18 and up, with Liquid Glass on iOS 26. Four shipping targets out of one codebase: the phone app, a watch app, and two widget extensions, sharing code through an App Group.
The backend. Cloudflare Workers (Hono, TypeScript) in front of Neon Postgres. Sign in with Apple.
Two AI engines, one voice. Cloud generation runs OpenAI through a Cloudflare AI Gateway. On-device generation runs Apple's Foundation Models framework, so that tier works on a plane and never sends a health value anywhere. Both engines eat the exact same system prompt, so the app sounds like one person either way.
The part I care most about: the AI never writes its own memory. A deterministic layer mines your Apple Health history for real patterns, your chronotype, how consistent your wake time is, seasonal shifts, milestones, and hands the model a summary. The model narrates. It never invents a fact about you. That means every single thing the app claims to know can be opened up, traced to the data behind it, edited, or deleted for good.
The daily numbers have no AI in them at all. Recovery, Effort and Rhythm are plain math on your own baselines, so they cost nothing to run and they're the same every time you look.
Money. RevenueCat runs every purchase through the SDK; my Worker stays the source of truth for entitlement. Two tiers, on-device and cloud, monthly, annual, and lifetime, and every plan starts with a free week (two on annual).
Challenges I ran into
HealthKit does not deduplicate anything. Wear a watch and a ring and both write sleep, so if you naively add them up you tell someone they slept fourteen hours. That became a per-source election for sleep and overlap detection for workouts. Then the nastier version: wake up at 3am, fall back asleep, and HealthKit stores two separate blocks, so the app confidently reported the second fragment as "your night" while the real night disappeared. Now fragments chain into one night.
A freeze I fixed twice before I actually fixed it. Users were hanging on launch. I moved the history mining off the main thread. Shipped it. Still hanging. Moved more off. Still hanging. What I'd missed is that this project sets SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor, and SwiftData's default @ModelActor runs its work inline on the caller's thread. So every "background" hop I'd written was still landing on main. It was a comfortable fiction and I'd read right past it twice. The fix was a hand-rolled actor with its own serial queue as the executor. The real lesson was to stop reasoning about threads and go measure them.
Launch day taught me something about feature flags. After going live, TestFlight sandbox purchases were still granting real entitlement, because the flag that stopped accepting sandbox purchases and the code that decided whether to honour stored ones had quietly drifted apart. Seventeen testers were holding a permanent lifetime unlock that no switch I owned could reach. In the end I deleted the flag completely, so there's nothing left to drift.
Accomplishments that I'm proud of
Getting four targets, a serverless backend, and two different AI engines to feel like one calm thing that speaks with one voice.
And the trust surface. It's a health app, so I decided early that everything had to be traceable: the typical ranges cite real institutions, the AI is structurally unable to invent medical facts or links, health values get scrubbed out of analytics and crash reports, and anything the app remembers about you is right there to read and erase.
Shipping Spanish and German properly. Localizing the UI alone would have been a half product, because this app is mostly generated prose. So the AI answers in your language too, while the prompts and everything fed into them stay pinned to English, so the model's footing doesn't move with your region.
And going from 1.0 to 1.8 in seven weeks, roughly one real update every week since launch.
What I learned
That the hard part of an AI product isn't the model. Almost all of my time went into deciding what it's allowed to see, what it's allowed to write, and what happens when it's wrong. The deterministic layer around the model ended up being more of the product than the prompt is.
Also that I should trust my own reasoning about concurrency a lot less than I did.
What's next for Simple Health
Deeper cross-metric correlations, and a proper way to explore everything the app has figured out about you.
And getting it in front of more people. The people who find it tend to stick around. Finding them is the hard part now.
Built With
- ai
- app-intents
- apple-push-notifications
- cloudflare
- cloudflare-workers
- foundation-models
- healthkit
- hono
- liquid-glass
- mixpanel
- neon
- openai
- postgresql
- revenuecat
- sentry
- sign-in-with-apple
- storekit
- swift
- swiftdata
- swiftui
- typescript
- vercel
- watchos
- widgetkit
Log in or sign up for Devpost to join the conversation.