Skip to content

offline sync

github-actions[bot] edited this page Jul 3, 2026 · 1 revision

Offline Sync — ɳTask Mobile

ɳTask is a free-forever local task manager. It must work fully offline. This document describes how offline sync works.

Architecture

Storage: MMKV

The offline queue is backed by react-native-mmkv (primary) with @react-native-async-storage/async-storage as an automatic fallback (e.g. Expo Go without a native build). The queue survives app backgrounding and force-quit because MMKV writes synchronously to a memory-mapped file.

Queue key: ntask:mutation-queue:v1

Queue Format

Each entry is a QueuedMutation:

{
  id: string;               // unique entry ID (timestamp + random)
  type: QueuedMutationType; // create_task | toggle_task | delete_task | ...
  payload: Record<string, unknown>;
  idempotencyKey: string;   // client-generated; sent as X-Idempotency-Key header
  enqueuedAt: number;       // unix ms
  retries: number;          // incremented on failure
}

Sync Flow

  1. User performs an action while offline (create, toggle, delete task).
  2. Mutation is enqueued via enqueue() in src/lib/offline-queue.ts.
  3. An optimistic placeholder appears in the UI immediately (for create).
  4. useNetworkState (src/hooks/useNetworkState.ts) detects network change via NetInfo.
  5. On reconnect, onReconnect callback fires → processQueue() is called.
  6. Each mutation is dispatched in FIFO order with its idempotency key.
  7. On success: entry removed from queue.
  8. On failure: retries incremented. After 3 failures: entry removed, user notified.

Idempotency

Every create/update mutation carries an X-Idempotency-Key header generated by src/lib/idempotency.ts. The key is deterministic within a 5-minute window based on mutation type + seed. If the same mutation is retried (e.g. after a network blip), the server returns the same response rather than creating a duplicate.

Offline UI

When isConnected is false:

  • OfflineBanner appears at the top of every screen showing the count of pending mutations.
  • Previously cached tasks remain visible (urql cache-and-network policy).
  • New tasks created offline appear immediately with a spinner + "Saving…" label (pending state).
  • Swipe-to-delete enqueues a delete mutation rather than executing immediately.
  • Toggle (complete/uncomplete) enqueues a toggle mutation.

Configuration

No configuration required. Offline sync is automatic and always-on. It cannot be disabled (it is a free core feature per the Security-Always-Free Doctrine).

Limitations

  • Conflict resolution: last-write-wins (server clock). If the same task is modified on two offline devices, the last sync wins.
  • Max retries: 3. After 3 failures a mutation is dropped and the user is notified.
  • Queue size: unbounded (MMKV storage limit is device storage).

Clone this wiki locally