-
-
Dot app icon (1024×1024)
-
Settings: "What we can't see", no servers, no account, no analytics
-
Insights: your phase explained, cycle stats with typical-range badges
-
Calendar: logged periods, fertile window and dashed predicted days
-
10-second check-in: flow, symptoms and mood as one-tap illustrated tiles
-
Today: honest "next period" range, cycle ring and phase (raw iPhone screenshot, 1179×2556)
-
Welcome: encrypted on this phone, no account, no cloud
Inspiration
I wanted to know what a period tracker would look like if it were built so that a breach was impossible rather than unlikely.
Most cycle apps are cloud products. You create an account, and the most intimate health data you own — when you bled, whether you had sex, whether it was protected — lives on someone else's server, covered by a privacy policy that can be rewritten, sold, or subpoenaed. Post-Roe, that stopped being an abstract concern.
The honest fix isn't a better privacy policy. It's not having the data. So Dot has no server, no account, and no analytics. Not "we promise not to look" — there is nothing to look at.
The second thing that bothered me: every tracker predicts your next period as a single confident day, and then is wrong. For irregular cycles it's wrong constantly. Dot shows an honest range that widens when your cycles are unpredictable, and says so.
What it does
Dot is a period tracker that keeps everything on your phone.
- Private by architecture — all cycle data sits in an AES-256 encrypted SQLite database (SQLCipher) on the device. The passphrase lives in the iOS Keychain. No account, no sign-up, no cloud, no ad SDK, no analytics SDK.
- Honest predictions — a weighted moving average over your last completed cycles with outlier rejection, and an uncertainty window of ±max(1, σ) days. When Dot doesn't know, Dot tells you.
- A 10-second check-in — flow, symptoms, mood, energy, sleep and intimacy as one-tap illustrated tiles. No feeds, no streaks, no guilt.
- Patterns by phase (Pro) — which symptoms actually show up in your luteal phase, computed on-device.
- Discreet by default — reminders never reveal anything on the lock screen; Face ID locks the app on background.
- Answers to the onboarding questions matter — tell Dot your cycle runs 35 days and cold-start predictions use 35, not a textbook 28. Say you're on hormonal birth control and the fertile-window tint disappears entirely.
How I built it
Expo SDK 57 (React Native 0.86, React 19), TypeScript, expo-router.
The architecture is strictly layered so the interesting logic is testable without a simulator: domain/ is pure TypeScript with zero React or database imports (cycle resolution, phase derivation, the prediction engine), application/ holds repositories, infrastructure/ holds the encrypted DB client, the notification scheduler and RevenueCat, and features/ holds the UI.
- Storage — expo-sqlite with
useSQLCipher: true, Drizzle ORM for typed queries and migrations, the key held in expo-secure-store. - Payments — RevenueCat (
react-native-purchases10.x) with aproentitlement, gated only oncustomerInfo.entitlements.active, never on product IDs. Monthly, annual with a 7-day trial, and a lifetime non-consumable. - Notifications — local only, via expo-notifications. There is no push server, because a push server would mean knowing when your period is due.
- Art — 43 custom illustrations generated once at build time by a script and bundled as PNGs. The shipped app makes no API calls, so the "nothing leaves your phone" claim survives the illustrations.
- Rendering — Skia draws the animated cycle ring, the phase timeline and the trend charts.
299 tests across 45 suites, concentrated on the domain layer where a wrong answer actually hurts someone.
Challenges I ran into
A bug only a real device shows you. I built most of it against the iOS simulator. On a physical Android emulator I found the settings switches were unreachable and the onboarding keyboard covered the Continue button — neither reproduced in my normal loop. Lesson learned the expensive way: the simulator is a rendering preview, not a device.
Apple Guideline 3.1.2(c). First rejection, because the paywall showed prices but not the length of each subscription. Fixing it meant restating the terms on the purchase screen itself, not just the store listing.
rescheduleAll will happily cancel everything. The reminder engine cancels all scheduled notifications and re-derives them from the database on every period log. Omitting a category from the call silently killed those alerts forever, and silent failures in a reminder system are invisible until a user misses something.
Encrypted-at-rest is a sequencing problem. SQLCipher needs PRAGMA key before any other statement touches the file. Get the order wrong and it reads as corruption, not as an auth failure.
Honest uncertainty is a design problem, not a math problem. The algorithm was ~100 lines. Making "we're not sure" feel calm and trustworthy instead of broken took several full redesign passes.
Accomplishments that I'm proud of
It's live. Dot shipped to the App Store and is at version 3.0.0 — real releases to real users, not a demo build.
The privacy claim is structural. I can say "your data never leaves your phone" because there is no network path for it to leave by — no analytics, no crash reporter, no telemetry, no server. Apple's privacy label is Data Not Collected.
And core tracking is free forever, in writing. Your history is never held hostage behind the paywall.
What I learned
That "private" is an architecture decision, not a marketing one — every feature request (cloud backup, partner sharing, AI insights) is really a request to weaken the one thing that makes the app worth using, and saying no is the product.
That shipping early and fixing on real devices beats polishing against a simulator.
And that users trust an app more when it admits uncertainty than when it pretends to be certain.
What's next for Dot
- Android release (the build is done, the Play listing is queued)
- Apple Health write-only export, so Dot can give data out without ever taking any in
- An equity plan so paying subscribers fund free Pro access for people in restricted-access states
- Wider symptom pattern analysis — still entirely on-device
Built With
- android
- drizzle-orm
- expo-notifications
- expo-router
- expo-secure-store
- expo.io
- ios
- jest
- react-native
- react-native-skia
- reanimated
- revenuecat
- sqlcipher
- sqlite
- typescript
Log in or sign up for Devpost to join the conversation.