05 / Case Study
Offline-First Rider App
A React Native delivery app for riders working without reliable connectivity, built on a local SQLite source of truth and a sync engine that routes each shipment by type and outcome.
The problem
Delivery riders lose connectivity constantly — basements, stairwells, lifts, rural routes. An app that needs the network to record a delivery is an app that blocks a rider mid-route, and a shipment outcome lost to a failed request is a package the system still believes is in transit. The requirement was not graceful degradation but genuine offline operation: a full shift's work captured with no signal at all, then reconciled without duplicating or dropping a single outcome.
Architecture
The on-device SQLite database is the source of truth, not a cache. Seven tables — shipments, orders, shipment details, product images, product attributes, QC questions and QC answers/images — hold everything a rider produces during a shift, and every action is written locally and stamped with a sync status before the interface acknowledges it. Nothing in the UI waits on a request.
When connectivity returns, a sync engine walks the pending shipments and dispatches each one on a two-dimensional key: shipment type and outcome. First mile, last mile, RTO and reverse pickup each settle through a different endpoint, and each has a distinct success and failed-attempt path — eight routes in total, because a failed pickup and a failed delivery are not the same event and neither resembles a completed one. Every dispatch carries its own success and failure callback, so a record that fails to sync stays pending and the rest of the queue drains past it.
Decisions
Local-first, sync-later
SQLite is treated as the system of record on-device rather than a read cache in front of the API. That inverts the usual mobile data flow: the network is a background reconciliation concern, not a precondition for the rider finishing a job.
Route by type and outcome, not through one generic queue
A single 'sync pending items' endpoint would have collapsed genuinely different settlements into one payload shape. Instead the engine dispatches on shipment type crossed with outcome, because each combination carries different evidence — manifest images and a signature for a pickup, signature and cash-collection proof for a delivery, QC question answers and photo sets for a reverse pickup.
Evidence capture belongs in the offline path
Barcode scanning, signature capture, QC photo sets and COD/UPI collection all persist locally alongside the shipment they belong to. Proof of delivery captured offline is worthless if it can't be tied back to the right record on sync, so the images are stored as rows against the shipment rather than as loose files.
Ship fixes without waiting on store review
CodePush checks for an update on app start. For a workforce app where a bug can strand riders mid-route, a two-day store review is not an acceptable turnaround for a hotfix.
Built for the people actually using it
Four locales — English, Hinglish, Hindi and Marathi, roughly 500 strings each. The users are working riders, and Hinglish in particular exists because romanised Hindi is what a lot of them actually read fastest.