← All articles

7 min readoffline-firstmobileCRDTsfield-operationssync

Offline-First Mobile Apps for Field-Based Businesses

Field workers don’t lose work because the app is bad. They lose it because the network dropped.

A delivery driver in a basement parking garage, a construction inspector on a remote site, a field technician in a rural area where signal bars flicker between one and two. These people generate data all day. If your architecture assumes a stable connection, you are building a system that fails at the exact moment it matters most.

The offline-first approach flips the assumption. The device becomes the primary data store. The server becomes a sync target, not a gatekeeper. This is not a nice-to-have for edge cases. For field-based businesses, it is the baseline requirement.

The device is the database

Most mobile apps treat the local storage as a cache. They fetch from the server, render, and write a local copy for speed. When the network dies, the app either shows stale data or throws an error.

Offline-first inverts this. SQLite or Realm on the device holds the canonical state. Every read and write happens locally, instantly, regardless of connectivity. The server exists to synchronize state across devices and to provide a durable backup, but the user never waits on it for basic functionality.

This means your data model design changes. You are no longer thinking “what do I fetch from the API?” You are thinking “what does this device need to know to operate autonomously?” For a field app, that might mean the full list of assigned tasks, the current status of each, and enough context to make decisions without a round-trip to the server.

The practical implication: your local schema is not a denormalized projection of the server schema. It is a purpose-built store optimized for the read patterns of the field workflow. You might denormalize aggressively. You might pre-compute values that the server would normally calculate. You are trading storage for latency, and on a modern phone, storage is cheap.

Conflict resolution without a central referee

The moment two devices edit the same record while offline, you have a conflict. The naive approach is last-write-wins, which is simple but destructive. A field technician updates a status code while a back-office manager updates the priority field. Last-write-wins silently discards one of those changes.

CRDTs (Conflict-free Replicated Data Types) solve this by designing data structures where concurrent operations are commutative and idempotent. The order in which operations are applied does not matter. Both changes survive. The merge is deterministic and does not require a central authority to arbitrate.

For most field apps, you do not need a full CRDT library. You need a few patterns:

  • LWW registers for single-value fields where the latest timestamp wins (acceptable for simple status fields).
  • G- counters for aggregate values that only increase (like a task count or hours logged).
  • OR-sets for collection fields where items are added and removed independently (like tags or assigned personnel).

The key insight is that you choose the conflict strategy per field, not per document. A status field might use LWW. A notes field might use a grow-only log that appends rather than overwrites. A priority field might use a bounded LWW with a manual override flag that surfaces the conflict to the user when they reconnect.

This is where domain knowledge matters. You need to understand what “conflict” means in your business context before you pick a data structure. A delivery app and a construction inspection app will have very different conflict profiles.

Background sync that respects the OS

Android’s Doze mode and iOS’s Background Fetch both exist to save battery. They both make it hard to guarantee that your sync happens in a reasonable timeframe.

The pattern that works in production is a layered approach:

  1. Foreground sync. When the app is open and the network is available, sync immediately. This is the happy path and should be fast (under 2 seconds for most field apps with reasonable payloads).
  2. Background sync with a budget. Schedule background sync attempts within the OS constraints. On Android, this means WorkManager with network constraints. On iOS, it means BGAppRefreshTask with a time budget. You accept that the OS may delay or skip your sync, and you design the UX to handle that gracefully.
  3. Opportunistic sync. Piggyback on any network activity. If the user opens the app even briefly, check for pending sync work and execute it in the background thread.

The critical design decision is what happens when sync fails or is delayed indefinitely. The app must remain fully functional. The user must be able to complete their work, close the app, and have the data sync later. If your flow requires a server confirmation to proceed, you have broken the offline-first contract.

Delta sync (sending only the changes since the last known state, rather than full documents) is essential for bandwidth efficiency. A field worker in a rural area might be on a 2G connection with a 50ms round-trip time and a 10KB/s ceiling. Sending a 50KB document when only one field changed is not just wasteful, it is a UX failure. The user watches a spinner for 45 seconds and assumes the app is broken.

Protecting on-device data

When the device is the primary store, it is also the primary attack surface. A lost phone now contains your business data, not just a cached copy that expires.

Two controls are non-negotiable:

  • Encryption at rest. SQLite supports SQLCipher for transparent encryption. Realm has built-in encryption. The key should be derived from the user’s authentication (biometric or PIN) and stored in the platform’s secure enclave (Keychain on iOS, Keystore on Android).
  • Remote wipe. Your server must be able to invalidate a device’s access and trigger a data wipe. This does not require the device to be online at the moment of the command. The next time the device connects, it checks for a revocation flag and purges local data.

For field-based businesses, this is not theoretical. Phones get lost on job sites. They get dropped in water. They get stolen from trucks. The cost of a data breach from a single lost device can exceed the cost of the app itself.

Testing for the conditions you actually face

Unit tests with a mocked network do not tell you how your app behaves when the packet loss is 40% and the latency is 800ms. You need a testing strategy that simulates real field conditions.

Practical approaches:

  • Network throttling in integration tests. Tools like Charles Proxy or the built-in network conditions in Android Studio let you simulate 2G, 3G, and high-latency conditions. Run your full user flows under these conditions.
  • Chaos testing for sync. Introduce random sync failures during test runs. Verify that no data is lost and that the app recovers cleanly.
  • Battery drain monitoring. An app that syncs aggressively will drain the battery of a field worker who is out for 10 hours. Profile your sync frequency and data volume under realistic usage patterns.
  • Cold start with corrupted local data. Simulate a crash mid-write. Verify that your local database recovers or that the app detects the corruption and re-syncs from the server.

The goal is not to test every possible network condition. The goal is to build confidence that the app degrades gracefully rather than failing catastrophically.

Where this fits

If you are building for a business that operates in the physical world, offline-first is not an architectural preference. It is a requirement that emerges from the reality of where your users work. The tools exist (CRDT libraries, encrypted local databases, delta sync frameworks), but the hard part is the design decisions: what is the source of truth, how do conflicts map to business meaning, and what does the user experience look like when the network is absent for hours?

These are engineering questions, but they are also product questions. They require understanding the workflow before you write a line of code. If you are exploring how to structure a mobile or web project around real operational constraints, the project case studies on gabrielepau.com show how these decisions play out in production systems built for small businesses and field operations.

The network will drop. Your app should not care.