leehyunhong
All projects
onsim

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.

onsim — Samsung Health data flow

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