Inspiration

Every diabetes tracker asks you for a number the person doesn't have.

"How many carbs was that?" — the whole app hinges on that field, and the honest answer for a plate of home-cooked food is "I have no idea." So people guess, guess wrong, and stop logging within a week. The tracking isn't the hard part. Knowing what you just ate is the hard part.

Blood Sugar starts at the plate: you photograph the food, and the app returns the carbs. Everything else — readings, insulin, medication, time in range — hangs off that one moment actually being frictionless.

What it does

  • Snap your plate → carbs. Photo in, grams of carbohydrate out.
  • CGM and meter readings sync themselves through Apple Health — a day of sensor data is a line, not 288 dots.
  • Log by voice. "Sugar 6.2 before lunch, 4 units" parses into a structured entry, on device.
  • Time in range, A1c estimate, patterns — and a clean PDF for your endocrinologist.
  • 40 languages, because diabetes is not an English-speaking condition.

How I built it

Client — 100% SwiftUI, no UIKit view layer. SwiftData for storage with CloudKit sync, Swift Charts for the glucose graphs, HealthKit for CGM/pump/meter ingestion, App Intents + WidgetKit for Siri and the home screen, PDFKit for the doctor's report, Core Haptics for feedback.

Two AI layers, deliberately split by cost and privacy:

  1. On device — Apple Foundation Models. Glycemic-index lookup and the voice-dictation parser run entirely on the phone. Free, offline, and the sentence "my sugar was 14 this morning" never leaves the device.
  2. In the cloud — Gemini 3.1 Flash Lite behind a Cloudflare Worker, only for the meal photo, where a small on-device model isn't good enough yet.

Nutrition labels go through Vision/VisionKit OCR rather than the model — a packaged label is structured text, and OCR is instant and free.

Server — a Cloudflare Worker in TypeScript. It holds the Gemini key (never shipped in the app), validates the user's subscription server-side with Apple's app-store-server-library, and meters scans per subscriber in a Durable Object.

Monetization — RevenueCat. Blood Sugar Plus gates the meal scanner and the PDF report. Glucose logging, CGM sync and medication reminders are free on purpose (see below).

Sentry + TelemetryDeck for observability, Xcode Cloud for CI.

Challenges I ran into

Somebody has to pay for every photo. A scan costs about \$0.0006. That's nothing until an extracted app secret loops a camera at it. The fix ended up being three ceilings rather than one: a per-IP rate limit stops one machine, a global limit caps the blast radius if the secret spreads, and the real hard stop is a prepaid balance on the Gemini side that no bug in my code can exceed. Free-tier KV couldn't do exact per-user counting — 1000 writes a day, one per second per key — so subscriber allowance moved to a Durable Object.

Nutrition labels read in the wrong order. OCR returned tokens sorted by the top of each bounding box, so a slightly rotated label interleaved two columns and "Carbohydrate 0" came out as "Carbohydrate 45". Sorting by reading order instead of geometry fixed it — the bug looked like a bad model and was pure coordinate math.

SwiftData got slow at real-world scale. A person with a sensor generates ~105k readings over a few years. Querying that per screen was hopeless, and the date index couldn't be added to an already-shipped store. Solution: a GlucoseDay aggregate table maintained incrementally, so the home screen reads days, not readings.

Time is a liar. Two of my nastiest crashes were calendar bugs: a ClosedRange built backwards when the day window changed while data was still loading, and a "now" captured in a computed property that never ticked, freezing the day view at whatever minute the app launched.

Deciding what not to charge for. The easy money is on the insulin log and the CGM sync — the features someone uses every single day, several times a day. I gated the AI scanner and the report instead. Paywalling the log of a chronic condition is a business model that only works if the user has no alternative, and that's not a thing I want to build.

What I learned

  • Split your models by where the data lives, not by capability. Anything containing "my sugar was 14" stays on device. Only an anonymous photo of food goes out.
  • A per-request cost is a security surface. Rate limits aren't polish, they're the difference between a hobby project and a five-figure bill.
  • Aggregate early. The performance work I'm proudest of is a table that stores 365 rows a year instead of 30,000.
  • Localization is a feature, not a chore. 40 locales, 634 strings — and units differ too: mmol/L vs mg/dL isn't a formatting preference, it's a safety issue.
  • The hardest part of a health app is respecting how boring the user's day already is. Every tap you remove is a day they keep logging.

Built With

  • app-intents
  • app-store-server-api
  • apple-foundation-models
  • cloudflare-workers
  • cloudkit
  • durable-objects
  • gemini
  • healthkit
  • ios
  • ipados
  • pdfkit
  • revenuecat
  • sentry
  • storekit
  • swift
  • swift-charts
  • swiftdata
  • swiftui
  • telemetrydeck
  • typescript
  • vision
  • visionkit
  • widgetkit
  • xcode-cloud
Share this project:

Updates

Submission history