Mobile apps live in unreliable network environments. Here's how I build apps that work regardless of connectivity.
The Core Pattern
Every data operation goes through a local-first pipeline:
1. Write to local SQLite immediately
2. Queue the operation for sync
3. Background sync when connectivity returns
4. Handle conflicts with last-write-wins or custom resolution
typescript
async function saveTransaction(tx: Transaction) {
// 1. Save locally first
await localDB.transactions.insert(tx);
// 2. Queue for sync
await syncQueue.push({
type: 'CREATE',
table: 'transactions',
data: tx,
timestamp: Date.now()
});
// 3. Trigger sync if online
if (navigator.onLine) {
syncManager.flush();
}
}Users don't care about your server's uptime. They care that the app works when they open it.
Conflict Resolution
For my finance tracker Koin, I use timestamp-based last-write-wins. It's simple and works for 99% of cases. For the remaining 1%, I show a merge dialog.