Supported on: iOS SDK
1.0.61+ · Android SDK 1.0.33+ · Flutter SDK 1.0.3+Config Resolution Priority
When the SDK needs configuration data, it follows a three-tier priority order:The SDK uses a stale-while-revalidate pattern. Cached config is served immediately while a fresh copy is fetched from the remote source in the background.
Config TTL
The cached configuration has a time-to-live (TTL) that defaults to 3600 seconds (1 hour). You can customize this value inAppDNAOptions:
When the TTL expires, the SDK fetches a fresh config from Firestore on the next access. The stale config continues to be used until the fresh config is received.
Config Update Notifications
The native layer emits a notification whenever the SDK applies a new remote config bundle (initial load, TTL refresh, or live Firestore push). UseAppDNA.remoteConfig.onChanged to react in Flutter:
Notification.Name(AppDNA.configUpdated) and Android’s AppDNA.configUpdated SharedFlow<Unit>.
Bundled Config
To ensure the SDK has a valid configuration on first launch — even without network connectivity — you can bundle a static config file with your app. The file should be downloaded from the AppDNA Console under SDK > Config Bundle.iOS
Add the config file to your Xcode project bundle:- Save the downloaded file as
appdna-config.json. - Add it to your Xcode project by dragging it into the project navigator.
- Ensure the file is included in your target’s Build Phases > Copy Bundle Resources.
Android
Place the config file in the Android assets directory:Bundle Versioning
The bundle version is managed by the native layer. When the SDK resolves config, it compares the cached config version with the remote version to determine whether an update is needed. The bundled config is treated as the lowest-priority source and is always superseded by any cached or remote config.Event Queue
Events tracked withAppDNA.track(...) are not sent immediately. They are queued locally on the native side and flushed in batches.
Queue Behavior
Events are flushed when either condition is met (whichever occurs first). You can also call
AppDNA.flush() to trigger an immediate flush.
Persistence
Events are persisted to disk immediately when tracked. This ensures that:- Events survive app crashes and force quits.
- Events survive app restarts and device reboots.
- Events are delivered on the next successful flush after connectivity is restored.
On iOS, the event queue is stored in the app’s Application Support directory. On Android, events are stored in a local SQLite database under the SDK’s private namespace. Events remain queued until they are successfully delivered to the AppDNA backend or the user revokes analytics consent.
Adaptive Batch Sizing
The SDK automatically adjusts batch size based on network conditions:- Wi-Fi: 100 events per batch
- Cellular: 50 events per batch
- Expensive cellular (roaming / data saver): 20 events per batch
Event Storage Limits
Events are stored locally with the following limits:- Maximum 10,000 events in queue
- Maximum 5MB disk usage
Retry Policy
When a flush attempt fails, the SDK retries with exponential backoff:
After 3 failed attempts, the request is abandoned and events remain in the queue for the next flush cycle.
Error Handling
Circuit Breaker
After 5 consecutive upload failures, event uploads are paused until the next app session (a foreground event resets the failure count and resumes uploads). A single failure does not pause uploads. Every failed flush counts toward the consecutive-failure total — whether a 4xx batch was dropped or a 5xx/network error left the batch queued for retry — and only the 5th failure in a row trips the circuit breaker.Network Timeouts
Consent and Offline Behavior
When analytics consent is set tofalse:
Consent state is persisted on the native side (UserDefaults on iOS, SharedPreferences on Android). If a user revokes consent and restarts the app, the SDK remembers the revoked state and continues to drop events until consent is explicitly granted again.
Secure Storage
The following data is stored securely on each platform:
On iOS, Keychain storage ensures these values persist across app reinstalls and are protected by the device’s hardware encryption. On Android,
EncryptedSharedPreferences uses AES-256-GCM via the Android Keystore, so values are protected by hardware encryption on supported devices.
EventChannel Streams
The SDK uses FlutterEventChannels to stream real-time data from the native layer. These streams work across the full app lifecycle, including when the app transitions between foreground and background states:
EventChannel streams automatically reconnect when the app returns from the background. You do not need to re-subscribe to streams after lifecycle transitions.
Offline Behavior Summary
Best Practices
- Always bundle a config file for first-launch reliability.
- Keep
configTTLreasonable — 1 hour (default) balances freshness with network efficiency. - Call
flush()before background — While the SDK handles this automatically, explicitly flushing before the app enters the background provides an extra safety net. - Do not assume network availability — Design your app UI to work with cached config values. Use
onReady()to know when remote config has been loaded.

