Update: 5 October 2026
FocusGuard is now presented as live in the Trinx portfolio, with a public landing. The May engineering notes below describe the earlier pre-launch state; they are not a current store-release checklist. Platform enforcement and permission behaviour still deserve device-specific testing.
FocusGuard: native screen-time tools for iOS and Android
Original engineering notes from May 2026
FocusGuard is a screen-time app that interrupts compulsive phone use with friction screens and a bank of motivational (and deliberately annoying) overlay messages. The problem is the obvious one: doomscrolling and the reflexive app-open that turns "I'll just check something" into forty minutes gone. The category is crowded with soft nudges that you dismiss without reading. FocusGuard's bet is that gentle reminders don't work, so it leans on OS-level enforcement and overlays designed to be a pain to ignore.
I built it for myself first. I wanted limits that the operating system actually enforces, not a banner I swipe away on autopilot. That single requirement — enforcement, not suggestion — drove most of the hard engineering decisions, including the one to throw away the first version and rewrite the whole thing natively.
The mechanic: warning, busted, lockout
The core of FocusGuard isn't tracking; plenty of apps show you a pie chart of your week and change nothing. The core is the interruption.
When you cross a per-app time limit, the app puts a full-screen overlay in front of the distracting app. There are three escalating states. A warning when you're approaching or just past a limit. A "busted" screen when you've blown through it and you're still scrolling. And a lockout that actually stands between you and the app. Each screen pulls from a message bank with tone, flavor, and accent variants, paired with reaction GIFs, so the nagging doesn't become wallpaper your brain learns to ignore. That last point is the whole behavioral-design thesis: a single static "you've reached your limit" message gets filtered out within a day. Rotating, slightly absurd, accent-flavored copy keeps the friction novel enough to register.
The messages and GIFs aren't baked into the binary. They sync from Firestore with version-diff caching, so I can update the tone, add new lines, or swap reactions without shipping an app update, and the client only re-fetches the collections that actually drifted. More on why versioned, diff-aware sync matters in the offline-first sync patterns I keep reaching for.
Two native apps, one platform problem
Enforcement is where "just use a cross-platform framework" falls apart, and it's the reason this repo is a rewrite.
The first version was React Native and Expo. It was fine for the dashboard and the charts. It was not fine for the part that matters, because the part that matters lives in OS internals that have no clean cross-platform bridge. On Android, enforcing a limit means reading usage stats, then using accessibility and overlay services to detect that a blocked app is in the foreground and draw an Activity on top of it. On iOS, the only sanctioned path is the Screen Time stack — FamilyControls, ManagedSettings, and DeviceActivity — with a custom Shield as the interruption surface, running in app extensions outside your main process.
None of that is a thin JS shim over a native call. It's deep, platform-specific, permission-heavy, background-execution territory where the two operating systems don't even agree on the shape of the problem. So I rewrote FocusGuard as two fully native apps: Android in Kotlin and Jetpack Compose, iOS in Swift and SwiftUI. The migration plan maps each React Native slice to its Kotlin and Swift equivalent, which made the rewrite a methodical port rather than a from-scratch gamble. Doing this as a solo dev is only feasible with the AI-assisted workflow I lean on across everything I ship — more on that in building 11 products as a solo founder.
The Firebase backend as a contract between platforms
Two native apps create an obvious risk: drift. Android and iOS can quietly start writing slightly different shapes of the same data until the "shared" backend is shared in name only.
I handle that by treating Firebase as a strict contract rather than a bucket. Both apps hit one backend — Auth, Firestore, Functions, Storage, Analytics, and Crashlytics. The Firestore security rules use field whitelists: a document write is rejected unless every field is on the allowed list with the right type. That turns the rules file into the schema both platforms have to obey. If the iOS app tries to write a field the Android app doesn't know about, the rules reject it, and I find out at write time instead of three weeks later when a chart renders garbage.
To keep those rules honest, they're behind a rules-test-first CI gate. The root of the repo is a small TypeScript project — tsx plus Vitest — whose job is to run tests against the Firestore rules in the emulator. A rules change that loosens a whitelist or breaks an existing constraint fails CI before it can ship. Writing the rules test first, then the rule, then the client code that depends on it, is the same red-green discipline I'd use for application logic, applied to the security boundary where it matters most.
The stack
Android is Kotlin 2.0 with Jetpack Compose for UI, Hilt for dependency injection, Room and DataStore for local persistence, Retrofit for networking, Vico for the insight charts, and WorkManager for the background monitoring that keeps usage tracking alive. iOS is Swift 5.10 and SwiftUI, with SwiftData for local storage and the FamilyControls / ManagedSettings / DeviceActivity trio for enforcement, split across two app extensions so the Shield and activity monitoring run where iOS requires them to.
Both sit on the single Firebase backend with its whitelist-enforced rules. The repo root carries the TypeScript tooling for the rules tests, and there's a separate Astro 5 landing page for marketing. The product UI and the entire message bank are English-only; the internal docs are in Romanian. It's an English-market product, authored in Romania — which is also why this article exists in both languages.
Where it's at, and what's next
FocusGuard is an MVP, pre-launch. I want to be precise about that, because screen-time apps are exactly the category where it's tempting to round "it works on my phone" up to "it's live."
What exists is real: a full native codebase on both platforms at versionCode 1 / 1.0.0, the enforcement and overlay mechanic working end to end, the Firestore content sync, the CI-gated rules tests, billing and store-setup docs, ASO keyword research, and the landing page. What does not exist is a confirmed store listing. I can't claim either app is published, and I won't quote download or user numbers, because there aren't any I'd stand behind.
The honest gaps are concrete. On Android the release keystore is still a template, so there's signing and Play Console work between here and a real release. On iOS the billing is genuinely unfinished — the StoreKit 2 side of FocusGuard Pro is marked TBD, and the plan is server-verified receipts flipping a pro flag, but "planned" is not "done." Android's Play Billing v7 path is further along, yet the whole monetization layer needs the kind of end-to-end testing you can only really do once you're inside the store review pipeline.
The other open question is the one every enforcement app eventually faces: how hard to make it to escape. Friction that's trivial to bypass is theater; friction that's impossible is a support nightmare the first time someone genuinely needs their phone. Tuning that line — how stubborn the lockout should be, how the accent-flavored copy lands over weeks instead of minutes — is the real product work left, and it's the part I can only learn from people who aren't me.
If you want to see how FocusGuard fits with the rest of what I'm shipping, the full project list lives here. And if it's the multi-platform sync plumbing you care about, the offline-first sync write-up is the one I'd read next. If you'd like to know when it actually hits a store, the newsletter is where I'll say so.