Skip to main content

Offline-First Architecture

AppDNA SDKs are designed to deliver a fully functional experience even when the device has no internet connectivity. Every feature — onboarding flows, paywalls, experiment assignments, event tracking — works offline by default.

Three-Tier Config Priority

When the SDK needs configuration, it resolves it using a strict fallback chain:
The default config TTL is 1 hour. On app launch, the SDK attempts a remote fetch with a 3-second timeout. If the fetch fails or times out, the SDK immediately falls back to cached config. If no cache exists (e.g., first launch with no connectivity), the bundled config is used. Your app is never blocked waiting for a network response.

Config TTL and Refresh

The SDK checks whether the cached config has expired on every app launch and every return from background:
1

App launches or returns to foreground

The SDK checks the age of the cached config against the configured TTL (default: 1 hour).
2

Remote fetch attempted

If the cache is stale (or does not exist), the SDK makes a background request to GET /api/v1/sdk/config-bundle.
3

Fallback on failure

If the remote fetch fails, the SDK continues using the cached config. If no cache exists, the bundled config is used.
4

Cache updated on success

If the remote fetch succeeds, the new config is written to disk and used immediately. UI components backed by config (paywalls, onboarding) update on the next presentation.

Event Queue

Events are never dropped. The SDK persists every event to disk before attempting delivery:
  1. When track() is called, the event is written to a persistent on-disk queue immediately.
  2. The queue is auto-flushed every 30 seconds or when it reaches 20 events, whichever comes first.
  3. If the flush fails (no connectivity, server error, timeout), events remain in the queue and are retried on the next flush cycle with exponential backoff.
  4. Events are only removed from the queue after the server returns a successful acknowledgment.
Because events are persisted to disk, they survive app restarts, force-quits, and even device reboots. No event is ever lost due to a crash or connectivity issue.

Backoff Strategy

When a flush fails, the SDK backs off before retrying. The base delays are [1, 2, 4] seconds, each with ±25% random jitter, and the SDK makes at most 3 retries per flush cycle before giving up and waiting for the next cycle: After the 3rd retry the flush is abandoned and retried on the next flush cycle. A server-supplied Retry-After header, when present, overrides the computed delay. The retry counter resets after a successful flush.

Storage per Platform

Each platform uses native storage mechanisms for maximum reliability:
The anonymous ID is stored in the most secure storage available on each platform (Keychain on iOS, Encrypted SharedPreferences on Android). This ensures the ID survives app reinstalls on iOS (as long as the Keychain entry is preserved) and is protected from unauthorized access.

Config Bundle Embedding

For zero-latency first launch, you can embed a config bundle JSON file in your app binary. The SDK automatically detects this file and uses it as the last-resort fallback when no remote or cached config is available.
Add appdna-config.json to your Xcode project as a bundle resource:
  1. Drag appdna-config.json into your Xcode project navigator.
  2. Ensure it is added to your app target under Build Phases > Copy Bundle Resources.
  3. The SDK automatically looks for this file in the main bundle on launch.
The embedded bundle is a fallback only. The SDK always prefers a fresher remote or cached config when available. Embed a bundle to ensure your app works correctly on first launch before any network request completes.

Offline Experiment Assignment

Experiments work fully offline because assignment is computed locally on-device using a deterministic hash function:
The same user always gets the same variant for the same experiment, regardless of whether the device has connectivity. All the SDK needs is the experiment definition (which is included in the config bundle) and the user’s ID. Once the config is available — whether from remote, cache, or bundle — the SDK can assign users to experiment variants and return results from getVariant() with zero network dependency.
Exposure events are still queued locally when offline and flushed to the server when connectivity returns. This means your experiment analytics remain accurate even when users interact with experiments while offline.

Module-Specific Offline Behavior