eduardweb.
Prezentări & ShowcaseIntermediar#react-native#mobile#expo#supabase#showcase

Am lansat un habit tracker în Expo: 200 de useri, stack și unde m-am fript

De Cristian Barbu, 14 oct. 2026 · 3 vizualizări · 2 like-uri

Postat acum 2 zile

Am pus pe App Store și Google Play o aplicație minimalistă de habit tracking construită în serile libere, mai mult de curiozitate. În prima lună s-au adunat vreo 210 useri fără marketing plătit, doar dintr-o postare pe Reddit și două fire pe Twitter. Vreau să povestesc sincer despre stack, costurile reale și deciziile arhitecturale care m-au mușcat de fund mai repede decât mă așteptam.

Stack-ul: de ce Expo și Supabase

Am mers pe Expo SDK 51 cu noul Expo Router, Zustand pentru state local și Supabase pe post de backend (Postgres + Auth).

EAS Build mi-a economisit zeci de ore de chin cu Xcode, certificates și provisioning profiles. Cine a trecut măcar o dată prin procesul manual de release pe iOS știe exact despre ce nervi vorbesc. Costul pe lună e fix 0 euro în faza asta: free tier-ul de la Supabase duce fără să clipească traficul ăsta, iar creditele gratuite de la EAS mi-au ajuns lejer pentru 12 build-uri de test și 3 de producție.

Expo Router v3 s-a comportat surprinzător de bine pe navigație clasică de tab-uri. Singurul trade-off a fost când am vrut să fac un modal semi-transparent peste un tab activ — deep linking-ul a început să facă figuri și a trebuit să scriu un hack urât în _layout.tsx ca să nu piardă starea ecranului din spate.

Unde m-am lovit cu capul de prag

Prima gafă majoră: am tratat modul offline superficial. Când faci un habit tracker, omul vrea să bifeze că a citit 10 pagini în metrou, fără semnal, să bage telefonul în buzunar și să nu-i pese. Eu m-am bazat pe Supabase JS Client și o sincronizare naivă peste noapte, ținând starea curentă într-un store de Zustand sincronizat cu AsyncStorage.

Rezultatul? Userii deschideau app-ul fără net, bifau trei chestii, dădeau force close din reflex, iar la următoarea deschidere datele dispăreau pentru că mutate-ul pica silențios în fundal. Am primit primele două recenzii de 1 stea în prima săptămână exclusiv din cauza asta. A trebuit să opresc tot și să rescriu logica cu o coadă de sync (outbox pattern) salvată local în SQLite.

Al doilea perete: notificările locale pe Android 14. expo-notifications merge strună în simulator și pe iPhone, dar pe Android-ul nou, permisiunea de SCHEDULE_EXACT_ALARM este un coșmar dacă userul are baterry saver activ. Reminderele de la ora 20:00 se declanșau uneori la 22:30 sau deloc. Într-un habit tracker, dacă notificarea nu vine la minutul stabilit, aplicația e complet inutilă.

Ce aș face altfel azi

Aș începe din prima secundă cu o arhitectură local-first. E de zece ori mai simplu să construiești un app care citește și scrie doar într-o bază SQLite locală (folosind Expo SQLite sau un wrapper gen OP-SQLite / WatermelonDB) și să sincronizezi incremental spre Postgres în fundal, decât să iei o aplicație clasică de tip CRUD-client și să încerci să-i lipești capabilități offline după lansare.

De asemenea, aș renunța la Zustand pentru datele de domeniu. Pentru temă, setări UI și tab-uri curente e brici. Când începi însă să gestionezi rezolvare de conflicte între timestamp-uri venite de pe două telefoane diferite ale aceluiași cont, Zustand devine un talmeș-balmeș greu de testat.

Una peste alta, 200 de oameni nu par mulți, dar vreo 45 dintre ei intră zilnic fără să le dau vreun push disperat. Voi ce soluție stabilă folosiți în producție pentru sync local-first în React Native?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.