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.

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.
This diagram could not be rendered. Its source is still available below.
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.
This diagram could not be rendered. Its source is still available below.
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.
Trends should describe, not diagnose
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.
This diagram could not be rendered. Its source is still available below.
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.