All notes

【TECH】Local-first health data design in MonoPump

How MonoPump keeps heart-rate history useful without turning private measurements into cloud data or medical claims.

privacylocal-firsthealth dataiOS
【TECH】Local-first health data design in MonoPump

A heart-rate app has two responsibilities that pull in different directions. It should remember enough history to be useful, and it should avoid treating personal health data like ordinary analytics. MonoPump chooses the local-first side of that tradeoff.

A phone with private heart-rate history represented as local protected files
Local-first design keeps the default measurement path private and simple.

The measurement algorithm gets most of the attention, but the data layer decides whether the product feels trustworthy after the result screen disappears.

The record is small on purpose

A saved reading does not need to become a medical dossier. MonoPump keeps the essentials: date, BPM, user, optional context, quality, confidence, duration, source, and notes. That is enough for history, export, and trend descriptions without collecting more than the app needs.

Entity relationship diagram Preparing diagram
View diagram source
erDiagram
    USER ||--o{ HEART_RATE_RECORD : owns
    USER {
        uuid id
        string name
        string avatar
    }
    HEART_RATE_RECORD {
        uuid id
        date measuredAt
        int bpm
        double qualityScore
        double confidenceScore
        string context
        string source
    }

The restraint is intentional. If a field does not improve the experience or the user has not asked for it, it should not be collected just because it is easy.

Local storage changes the product boundary

MonoPump stores records in the app container using JSON files under Application Support, written atomically and with complete file protection. UserDefaults remains for lightweight preferences and migration compatibility, but the durable history lives in local files.

Flow diagram Preparing diagram
View diagram source
flowchart LR
    A[Measurement result] --> B[HeartRateViewModel]
    B --> C[Encode records as JSON]
    C --> D[Atomic write]
    D --> E[Application Support]
    E --> F[iOS file protection]

This is not only an implementation detail. It means the default path does not need an account, server, sync quota, or network permission. For a small health utility, that simplicity is a feature.

The health analysis layer is deliberately modest. It computes average, median, min, max, recent-window comparison, active days, and quality summaries. It does not infer HRV, stress, sleep quality, disease risk, or treatment advice.

Flow diagram Preparing diagram
View diagram source
flowchart TD
    R[Saved BPM records] --> S[Sort by date]
    S --> A[Average and median]
    S --> M[Min and max]
    S --> W[Recent vs previous window]
    S --> Q[Quality summary]
    A --> O[Descriptive insight]
    M --> O
    W --> O
    Q --> O

A consumer camera app can help a person notice patterns, but it should not pretend to be a clinician. The rule I use for MonoPump is simple: collect little, store locally, explain cautiously, export deliberately.

From MonoWare

MonoPump has more context behind this note.

Open the product site or keep reading notes filtered to MonoPump.

Newsletter

Privacy and product engineering notes, sent occasionally.