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:| Priority | Source | Description | Latency |
|---|---|---|---|
| 1 | Remote | Live fetch from the AppDNA API. Always preferred when network is available. | ~100-300ms |
| 2 | Cached | The last successfully fetched config, persisted to local storage on device. | ~1ms |
| 3 | Bundled | A static JSON file embedded in the app binary during CI/CD. Used on first launch or when no cache exists. | ~1ms |
The default config TTL is 5 minutes. 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:App launches or returns to foreground
The SDK checks the age of the cached config against the configured TTL (default: 5 minutes).
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.Fallback on failure
If the remote fetch fails, the SDK continues using the cached config. If no cache exists, the bundled config is used.
Event Queue
Events are never dropped. The SDK persists every event to disk before attempting delivery:- When
track()is called, the event is written to a persistent on-disk queue immediately. - The queue is auto-flushed every 30 seconds or when it reaches 20 events, whichever comes first.
- 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.
- 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:| Attempt | Delay |
|---|---|
| 1 | 1 second |
| 2 | 5 seconds |
| 3 | 30 seconds |
| 4 | 2 minutes |
| 5+ | 5 minutes (max) |
Storage per Platform
Each platform uses native storage mechanisms for maximum reliability:| Platform | Config Cache | Event Queue | Anonymous ID |
|---|---|---|---|
| iOS | UserDefaults | UserDefaults | Keychain |
| Android | SharedPreferences | SharedPreferences | Encrypted SharedPreferences |
| Flutter | Platform channels to native storage | Platform channels to native storage | Platform channels (Keychain on iOS, Encrypted SharedPreferences on Android) |
| React Native | Platform channels to native storage | Platform channels to native storage | Platform channels (Keychain on iOS, Encrypted SharedPreferences on Android) |
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.- iOS
- Android
- Flutter
- React Native
Add
appdna-config.json to your Xcode project as a bundle resource:- Drag
appdna-config.jsoninto your Xcode project navigator. - Ensure it is added to your app target under Build Phases > Copy Bundle Resources.
- The SDK automatically looks for this file in the main bundle on launch.
Offline Experiment Assignment
Experiments work fully offline because assignment is computed locally on-device using a deterministic hash function: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
| Module | Offline Behavior |
|---|---|
| Onboarding | Flows are presented from cache or bundle. Step completion responses are queued and synced later. |
| Paywall | Paywalls render from cache or bundle. In-app purchases require connectivity to complete. |
| Push | Push tokens are cached locally. Permission requests work offline. Token registration is queued. |
| Billing | Entitlements are served from cache. Purchase and restore operations require internet. |
| Tracking | All events are queued to disk and flushed when connectivity returns. |
| Remote Config | Values are served from cache or bundle. Changes arrive on next successful sync. |
| Experiments | Variants are assigned offline using MurmurHash3. Exposures are queued. |
| In-App Messages | Messages are presented from cached rules. Impression events are queued. |
| Surveys | Surveys are presented from cache. Responses are queued and synced later. |