Back to Blog
Mobile Apps

Offline-First Mobile Architecture: What Most Tutorials Get Wrong

Zyptr Admin
27 May 2024
9 min read

Why Offline-First Matters (Especially in India)

We build a lot of apps for the Indian market, and connectivity is not a binary state here. Your user might have 4G in Bengaluru, switch to intermittent 3G on the metro, lose signal entirely in an elevator, and reconnect on WiFi when they reach the office. An app that only works online will frustrate these users constantly. An app that works offline and syncs seamlessly feels like magic.

We've built four offline-first apps in the past two years — a field sales tool, a delivery tracking app, an inspection checklist app, and a healthcare data collection app used in rural areas. Every one of them taught us something the tutorials don't cover.

The Database Choice Matters More Than You Think

Most tutorials use AsyncStorage or SQLite and call it a day. For simple key-value caching, AsyncStorage works fine. But for apps with complex data relationships and the need for efficient querying offline, you need a real embedded database. We've settled on WatermelonDB for React Native projects — it's built on SQLite but adds a reactive layer, lazy loading, and importantly, a sync engine.

WatermelonDB's sync protocol is the real value. It handles the "pull changes from server, push local changes to server, resolve conflicts" cycle that you'd otherwise have to build yourself. We tried building custom sync twice before discovering WatermelonDB, and both custom implementations had subtle bugs that took weeks to find. The classic: a record gets updated on both client and server while offline, and the sync overwrites one version without the user knowing.

Conflict Resolution Is Where Dreams Die

The tutorial shows you how to save data locally and sync when online. What it doesn't show you is: what happens when User A edits a record offline, and User B edits the same record independently? Who wins? The answer depends on your domain, and there's no one-size-fits-all solution.

For our field sales app, we use "last write wins" with field-level granularity — if User A updates the phone number and User B updates the address, both changes are kept. Only if they edit the same field does the latest timestamp win. For the healthcare app, we use a stricter approach — conflicts are flagged for manual review because overwriting medical data silently is not acceptable.

We've also learned to design schemas that minimize conflicts. Instead of updating a "status" field (which creates write conflicts), we append status change events to an array. The current status is derived from the latest event. This event-sourcing-lite approach eliminates most conflicts because appends don't conflict with each other.

The Sync Queue Is More Complex Than You Think

Actions taken offline need to be queued and replayed when connectivity returns. This sounds simple until you consider: what if the user deletes something they created offline? Do you send the create followed by the delete, or skip both? What if action B depends on the server-generated ID from action A? What if the sync fails halfway through — do you retry from the beginning or resume from the failure point?

We use a priority queue for sync operations: creates before updates before deletes, with dependency resolution. If creating a "visit report" depends on a "customer" record that hasn't synced yet, the customer sync goes first. We learned this the hard way when a delivery app crashed because it tried to sync a delivery completion event before the delivery assignment had synced, and the server rejected it as an invalid reference.

Testing Offline Is Non-Negotiable

You cannot test offline behavior on a simulator with WiFi turned off. That doesn't simulate the real-world scenario of intermittent connectivity where requests sometimes succeed and sometimes timeout after 30 seconds. We use a custom network conditioner that simulates: complete offline, intermittent (50% packet loss), slow 2G (250Kbps with 2s latency), and "flaky" (random disconnections every 10-30 seconds). Every feature gets tested against all four conditions before release.

Our QA team also does "field testing" for apps deployed in rural India — literally driving to areas with poor connectivity and testing the app for a full day. We've found bugs in field testing that no simulator could reproduce, particularly around cellular handoff between towers and the behavior of HTTP requests that hang indefinitely on flaky connections.

offline-firstmobile-architecturesyncreact-native
Let's Work Together

Have a Project in Mind?
Great?

Let's talk about building your next product.