
Digital Healthcare · Wearable
onsim 온심
Heart-rate-based personalised exercise prescription and real-time zone coaching
- Client
- HB Connect
- Period
- Mar 2024 — present (backend since Jul 2023) · App 2.5+ years · in operation
- Role
- Lead for app, wearables and admin console; sole backend owner since Aug 2026
- Team
- 1 engineer (me) + product owner · sports-medicine advisor
- Platforms
- iOS · watchOS · Android · Wear OS · Web (Admin)
- Status
- Live on App Store & Google Play, v2.0.0
3,000+
Cumulative users
845 files
Dart
210K+ lines
Hand-written code
31K lines
Swift · Kotlin
7,211
Commits
13
Languages
Overview
Project overview
onsim started from an idea by a sports-medicine professional who has worked with national-team and professional athletes. Based on a health survey and personal profile it proposes a target heart-rate band (the 'onsim zone') and coaches users to stay inside it during workouts.
FITT prescriptions for 12 condition axes — cancer, cardiac, hypertension, diabetes, kidney, liver, sarcopenia, obesity and more — are encoded from the ACSM Guidelines 12th edition, and four heart-rate sources (phone, Apple Watch, Wear OS, BLE sensors) are arbitrated in one pipeline. It has grown into a full platform with social workouts, a rewards hub, trainer chat and AI feedback.
My Role
Responsibilities
- 01Designed and built the entire Flutter app (845 Dart files, ~210K hand-written lines)
- 02Standalone watchOS app (SwiftUI), workout Live Activity widget and watch-face complications — ~18K lines of Swift
- 03Standalone Wear OS module (Compose) with Samsung Health Sensor SDK partnership integration — ~13K lines of Kotlin
- 04Designed 7 Flutter ↔ native channel contracts: HealthKit, Health Connect, BLE, haptics, TTS, Naver login and more
- 05Firebase Functions (20K lines of TypeScript) — callables, FCM, social aggregation, AI prompts
- 06Operated the NestJS backend (110K lines) with AWS CDK; migrated 134 Grafana panels into an in-house admin dashboard
- 07Rebuilt the admin console from vanilla JS to Next.js + Tailwind on Firebase App Hosting
- 0813-language i18n, App Store review handling, release-branch strategy and build-number automation
Features
Key features
Personalised prescription engine
FITT prescriptions across 12 condition axes, conservative merging for multiple conditions, medication checks, renewal cycles and a senior prescription group. Issuance is server-owned; the app consumes results under a version-stamp contract.
Real-time onsim-zone coaching
Zone colour ring and pulse visualisation, haptic coaching on zone exit, iOS Live Activity and Android ongoing notification, GPS track with splits and pace.
Four heart-rate sources, one pipeline
Phone, Apple Watch, Wear OS and BLE HR/RSC sensors handled identically. Samsung Health integration approved and signed as a partner.
Chat-style onboarding and condition bot
Terms → PAR-Q → personal survey → cancer sub-questions → prescription preview in one flow. A daily condition check yields 'today's recommended heart rate' with RPE and talk-test adjustment.
Health records & PHR
HealthKit / Health Connect daily and weekly aggregates, sleep and menstrual data behind explicit consent, health-checkup XML/FHIR upload.
Social, rewards and chat
Live climbing track, friends and feed, missions, point shop and a virtual pet, trainer chat and the 'onstep' AI weekly report.
Architecture
Design principles
Two isolated backends
The in-house NestJS REST API and Firebase serverless are fully separated at the app-module level. Backend calls only pass a foreground gate; background changes drain on resume.
Native channel contracts
onsim/health, onsim.watch, onsim.ble, live_activity, haptics and other channels are documented and implemented with identical semantics on iOS and Android.
Encrypted local cache
All local file caches are encrypted with ChaCha20-Poly1305 and the key is sealed to the signed-in account. Sign-out destroys the key — cryptographic erasure.
Watch trust model
'The watch is the source of truth for watch workouts.' Liveness heartbeats, ghost-session rescue, stale-command rejection and reliable Data Layer delivery, ported to both watchOS and Wear OS.

Data-flow diagram from the Samsung Health Data SDK partner submission — consent gate → on-device processing → encrypted storage → revocation and deletion.
Challenges
Engineering challenges
Health Connect pagination loss
Problem
readRecords returns at most 1,000 records per call and only the first page was read. For sources like Samsung Health that split heart rate finely, the most recent part of the weekly window was cut off entirely (a real 4-minute sensor gap showed as 17 minutes unmeasured).
Solution
Replaced with a readAllRecords that walks every pageToken and shipped a one-time purge migration for already-polluted caches.
Result · Weekly workout accuracy restored, related support tickets closed
Telling 'failure' from 'zero records'
Problem
iOS native reads swallowed failures as empty arrays, so an app revived in the background on a locked device received zero records for every type and yesterday's workouts vanished for the rest of the process.
Solution
Errors now propagate to Dart, and zero-record responses while not in the foreground are never accepted as authoritative — a second line of defence.
Watch ↔ phone sync reliability
Problem
An auto-launch loop where phone and watch woke each other endlessly, a 25-second scene-watchdog SIGKILL from leftover HealthKit background delivery, a 32-bit Int trap at launch and NSNull payload crashes had accumulated.
Solution
Introduced liveness heartbeats, stale-command rejection, AOD elapsed-time handling and ghost-session rescue, locked down in an 840-line design doc. The same design was ported to Wear OS with three-source HR arbitration.
Six rounds of performance work
Problem
Android workout-history loading, cold start, landing repaint and idle heat were driving user complaints.
Solution
Diagnosed and fixed step by step — Dart → pipeline → native → shared glass snapshots → decorative animation policy → stopping frame generation when idle — and documented why RepaintBoundary alone could not solve it.
Four rounds of Firestore cost reduction
Problem
Social and ranking features caused Firestore read costs to spike.
Solution
Functions-side caching, ranking debounce, single client listener and a persistent cache, applied in sequence with a read-audit document as evidence.
Privacy and regulatory compliance
Problem
Sensitive data such as sleep and menstrual records were silently back-filled, uids could surface on social screens, and a mis-classified dev build once deleted real accounts.
Solution
Moved to explicit consent for everyone with a SensitiveConsentManager that blocks collection of unconsented types, introduced publicId across all social surfaces, encrypted every local cache and added a one-hour guard on account deletion.
App Store review and release strategy
Problem
A Sign in with Apple name re-entry issue (guideline 2.5.4, ITMS-90683) and a prod-merge routine that collided with a never-empty review window.
Solution
Fixed at the root via displayName preservation → name cache → confirmation step → SIWA revoke. Switched to v{version} branches as release endpoints and moved the build-number source of truth to TestFlight.
Tech Stack
Technology
App
- Flutter 3.44 · Dart 3.12
- Provider
- 10 Firebase products (Auth · Firestore · Functions · FCM · Crashlytics · Remote Config …)
- Kakao · Naver · Google · Apple · email login
- flutter_secure_storage · pointycastle
- 13-language l10n
Native
- Swift · SwiftUI (watchOS, Live Activity, complications)
- Custom platform views for iOS 26 Liquid Glass
- Kotlin · Jetpack Compose (Wear OS)
- Samsung Health Sensor SDK
- HealthKit · Health Connect · BLE
- SPM (migrated off CocoaPods)
Backend
- NestJS · TypeScript
- AWS CDK · ALB · ECS
- Firebase Functions (asia-northeast3)
- Sentry · k6 load tests
- GitHub Actions
Admin · Ops
- Next.js · React · Tailwind (Firebase App Hosting)
- Grafana → in-house dashboard
- 96 handoff · 649 worklog documents
Outcomes
Outcomes
- ✓Grew to 3,000+ cumulative registered users, operated continuously for 2.5 years on App Store and Google Play from v1.5 to v2.0.0 (build 783) without downtime
- ✓iOS, watchOS, Android and Wear OS plus the admin web maintained single-handedly
- ✓Samsung Health Data SDK partner approval obtained (read-only heart-rate data-flow submission)
- ✓Removed external dependencies — adaptive_platform_ui, Grafana, Prometheus, CocoaPods — to cut operational load
Next Project
momiplan 마미플랜