Skip to main content
NinjaNext
Mobile apps

Offline Mode Is Easy. Syncing Afterwards Is the Hard Part.

Letting people keep working without a connection is straightforward. Deciding what happens when two of them changed the same record is where offline features actually get difficult.

By NinjaNext TechnologiesPublished 4 min read

Key takeaways

  • Storing data locally is the easy half. The hard half is reconciling it when the connection returns.
  • Last write wins is a decision, not a default: it silently discards somebody's work.
  • Give every change an identity so a retry cannot apply the same action twice.
  • A flaky connection is harder than no connection, and it is what your users actually have.

Offline support usually gets specified in a single line: it should work without a connection. The local storage part of that is a solved problem. What is not solved, and what decides whether the feature is trusted or quietly switched off, is what happens in the seconds after the connection comes back.

The easy case, and why it misleads

If one person uses an app on one device and nothing else writes to their data, offline is genuinely simple: keep the data on the device and you are finished. Several of our own apps work this way, with everything stored locally and no account at all.

That experience is misleading, because it teaches you that offline is a storage problem. The moment two people, or one person on two devices, can change the same record, it becomes a disagreement problem.

Two people changed the same record

This is the question every offline feature has to answer, and most skip. Someone edits a job on a tablet with no signal. A colleague edits the same job at the desk. Both reconnect. Which version is right?

The usual answer is whichever arrived last, because it requires no thought. It is a legitimate choice for some data, and a bad one for most: it throws away a colleague's work with no record that it happened, and the person whose edit vanished has no way to know. If you choose it, choose it deliberately and tell users what it does.

The alternatives are worth knowing. You can merge at the field level, so two people editing different parts of the same record both keep their changes. You can reject the later change and ask the person to redo it against the current version, which is honest but interrupts them. You can keep both and flag it for a human. Which one is right depends entirely on what the data means, and that is a product decision rather than a technical one.

On the SwiftFix Auto workshop platform, the admin app queues changes made offline and syncs them when the connection returns. Conflicts resolve predictably rather than by accident, and the outcome is written to an audit log, so the question of who changed what, and when, is never a matter of opinion.

The same change applied twice

The second classic failure is quieter and worse. A queued change is sent, the server accepts it, and the response is lost on the way back. The app sees no confirmation and retries. Now the job has been created twice, or the stock has been decremented twice.

The fix is to give every queued change its own identifier, generated on the device before it is sent. The server records which identifiers it has already applied and ignores repeats. This is not difficult, but it has to be designed in: it cannot be retrofitted after someone notices duplicate records in production.

A bad connection is worse than none

Testing with the network switched off is the test everybody runs, and it is the gentle one. The app can tell what has happened, so it can act decisively.

What actually happens to your users is worse. A connection that is technically present but unusable: a request that will eventually time out after thirty seconds, a captive portal in a hotel that answers every request with a login page, a lift. In those conditions an app that waits politely for a response looks frozen, which users read as broken.

Set timeouts shorter than you think you need, treat a timeout as offline and queue the work, and make the state visible: saved on this device, sending, sent. People will forgive a delay they can see. They will not forgive a spinner that never resolves, and they will not trust the app with anything important afterwards.

When offline is not worth building

Offline support is a real cost, in build time and in every feature you add afterwards, because each one has to answer the same questions again. It earns that cost when the work genuinely happens where the signal does not: a workshop, a warehouse, a van, a basement.

If your users are at a desk on reliable wifi, a clear error message and a retry button will serve them better than a sync engine, and the budget is better spent elsewhere. Deciding this honestly at the start is worth more than any implementation detail in this article.

If you are weighing it up for a product you are building, tell us what you are building and we will give you a straight answer about whether it is worth it. You can also see how we approach this kind of work on our web app development page.

Let's build it

Want help putting this into practice?

Tell us what you are building and where you are stuck. We will reply with questions, a suggested approach and next steps.