Identity & Device IDs
The Datafly mobile SDKs use a multi-layered identity model to track users across sessions, app reinstalls, and the transition from anonymous to known user.
Identity layers
| Layer | What it is | Who sets it | Persistence |
|---|---|---|---|
| Anonymous ID | UUID v4 generated on first launch | SDK (client) | iOS: Keychain / Android: SharedPreferences |
| Device ID | IDFV (iOS) or ANDROID_ID (Android) | OS (automatic) | System-managed |
| IDFA / AAID | Advertising identifier | App (after consent) | System-managed |
| User ID | Your user’s database ID | App (via identify()) | Memory (cleared on reset()) |
| Canonical ID | Unified server-side identity | Server (automatic) | Redis + PostgreSQL |
How it works
1. App install (new user)
On first launch, the SDK:
- Generates a new anonymous ID (UUID v4)
- Collects the device ID (IDFV on iOS, ANDROID_ID on Android)
- Self-generates vendor tracking IDs (GA4
client_id, Metafbp, TikTokttp, Pinterest_pin_unauth) - Starts session 1
The first event sent to the server creates a canonical identity that links all these signals:
canonical:org123:uuid-canonical-1
├── link:dfid:uuid-anonymous-1 (anonymous ID)
├── link:idfv:uuid-vendor-1 (device ID)
└── vendor_ids: { ga_client_id, fbp, ttp, pin_unauth }2. User logs in
When the app calls identify("user-123"):
canonical:org123:uuid-canonical-1
├── link:dfid:uuid-anonymous-1
├── link:idfv:uuid-vendor-1
├── link:user_id:user-123 (now linked)
└── vendor_ids: { ga_client_id, fbp, ttp, pin_unauth }All pre-login events are now attributed to the identified user.
3. App reinstall
When the app is reinstalled, a new anonymous ID is generated. The server recovers the identity through device recognition:
New anonymous ID: uuid-anonymous-2
Device recognition hash matches → same device
Server links: dfid:uuid-anonymous-2 → canonical:uuid-canonical-1
Result: same canonical identity, continuous user historyDevice recognition
The server computes a SHA-256 hash from multiple device signals to identify returning devices:
| Signal | Weight | Source |
|---|---|---|
| IP address | 25 | Request headers |
| User agent | 25 | SDK-generated (includes device model, OS version) |
| Screen resolution | 15 | Device info |
| Accept-Language | 15 | Request headers |
| Timezone | 10 | Device locale |
| Locale | 10 | Device locale |
If the hash matches a previously seen device (confidence threshold met), the server links the new anonymous ID to the existing canonical identity.
Device recognition includes a collision cap (default 15 distinct anonymous IDs per hash). If exceeded — for example on a corporate network where many devices share the same IP and user agent — the hash is marked unreliable and device recognition is disabled for that fingerprint.
Identity correction
After processing device recognition, the server may return a corrected_anonymous_id in the batch response. The SDK automatically updates its stored anonymous ID to match:
SDK sends event with anonymousId: "uuid-anonymous-2"
Server recognises device → returns corrected_anonymous_id: "uuid-anonymous-1"
SDK updates Keychain/SharedPreferences with corrected ID
Subsequent events use the corrected IDPlatform-specific persistence
iOS
| Data | Storage | Survives reinstall | Survives factory reset |
|---|---|---|---|
| Anonymous ID | Keychain (kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly) | Yes | No |
| Anonymous ID backup | UserDefaults | No | No |
| IDFV | System | Yes (same vendor) | No |
| Vendor IDs | UserDefaults | No | No |
| Session number | UserDefaults | No | No |
The Keychain recovery chain: Keychain → UserDefaults backup → generate new.
Android
| Data | Storage | Survives reinstall | Survives factory reset |
|---|---|---|---|
| Anonymous ID | SharedPreferences | No | No |
| ANDROID_ID | System (per-signing-key since Android 8.0) | Yes | No |
| Vendor IDs | SharedPreferences | No | No |
| Session number | SharedPreferences | No | No |
Android has weaker client-side persistence than iOS. On app reinstall, a new anonymous ID is generated and vendor IDs are regenerated. The server-side device recognition system compensates by linking the new anonymous ID back to the existing canonical identity.
Vendor ID generation
On first launch, the SDK self-generates tracking IDs that match the format each vendor expects:
| Vendor | ID name | Format | Example |
|---|---|---|---|
| GA4 | ga_client_id | {random}.{timestamp} | 7234856102.1712498234 |
| Meta | fbp | fb.1.{timestamp_ms}.{random} | fb.1.1712498234567.8765432109 |
| TikTok | ttp | 27-char base36 | abcdefghijklmnopqrstuvwxyz1 |
pin_unauth | UUID v4 | 6f4f9e2a-1b3c-4d5e-8f9a-0b1c2d3e4f5a |
These are sent once on the first event, then cached server-side. This means you don’t need to embed Meta SDK, GA4 SDK, etc. in your app — Datafly generates compatible IDs natively.
Advertising IDs (IDFA / AAID)
Advertising IDs require explicit user consent and must be set by the app after permission is granted:
iOS (IDFA)
import AppTrackingTransparency
import AdSupport
ATTrackingManager.requestTrackingAuthorization { status in
if status == .authorized {
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
Datafly.shared.setAdvertisingId(idfa, enabled: true)
}
}Android (AAID)
// Using Google Play Services (separate dependency)
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
if (!adInfo.isLimitAdTrackingEnabled) {
Datafly.setAdvertisingId(adInfo.id, enabled = true)
}Never call setAdvertisingId() without first obtaining user consent. On iOS, this requires the App Tracking Transparency (ATT) prompt. On Android, respect the user’s “Opt out of Ads Personalization” setting.
Logout / reset
Call reset() when the user logs out:
Datafly.shared.reset()This:
- Generates a new anonymous ID
- Clears user ID and traits
- Clears group ID and traits
- Regenerates vendor IDs
- Does not clear the event queue (pending events are still delivered)