Offline-first sync with Drift and Firestore
Healthcare and military logistics are full of basements, rural clinics, and warehouses where the WiFi disappears. If the app does not work without a connection, it does not work. So all three apps treat the local database as the source of truth and the cloud as a best-effort mirror.
The shape of it
Local writes hit Drift (SQLite) immediately and the UI updates from a Drift stream. A separate OfflineAware repository watches the same data and pushes it up to Firestore when the network is healthy. The user never waits on the cloud.
Three patterns make this feel reliable instead of janky:
1. Debounced sync
If you scan ten supplies in a row, you do not want ten separate Firestore writes. Each local change starts a 5-second timer; rapid changes reset it; only when the user pauses does the batch upload. This single change cut my Firestore write costs by an order of magnitude.
2. Deletion tombstones
The first time I shipped sync, deleting a supply on one device caused it to reappear from the cloud listener on the other. Obvious in hindsight. Now every delete writes a row to a deleted_items table before the local row is removed; the sync layer reconciles tombstones with the Firestore listener so the item stays gone.
3. Version vectors per row
Each row carries a syncVersion integer that increments on every write. Conflicts compare versions instead of timestamps — clocks lie, integers do not. Combined with a pendingSync flag, this protects local edits from being overwritten by an in-flight Firestore push.
Where it still hurts
Two-device race conditions during a slow upload are the trickiest case. Most of the time the version-vector wins it, but occasionally a user sees a stale value for a few seconds before the next sync tick. I would rather take that than the alternative — surprising users with disappearing data.
— Peter Le