Mobile SDKsIdentity & Device IDs

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

LayerWhat it isWho sets itPersistence
Anonymous IDUUID v4 generated on first launchSDK (client)iOS: Keychain / Android: SharedPreferences
Device IDIDFV (iOS) or ANDROID_ID (Android)OS (automatic)System-managed
IDFA / AAIDAdvertising identifierApp (after consent)System-managed
User IDYour user’s database IDApp (via identify())Memory (cleared on reset())
Canonical IDUnified server-side identityServer (automatic)Redis + PostgreSQL

How it works

1. App install (new user)

On first launch, the SDK:

  1. Generates a new anonymous ID (UUID v4)
  2. Collects the device ID (IDFV on iOS, ANDROID_ID on Android)
  3. Self-generates vendor tracking IDs (GA4 client_id, Meta fbp, TikTok ttp, Pinterest _pin_unauth)
  4. 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 history

Device recognition

The server computes a SHA-256 hash from multiple device signals to identify returning devices:

SignalWeightSource
IP address25Request headers
User agent25SDK-generated (includes device model, OS version)
Screen resolution15Device info
Accept-Language15Request headers
Timezone10Device locale
Locale10Device 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 ID

Platform-specific persistence

iOS

DataStorageSurvives reinstallSurvives factory reset
Anonymous IDKeychain (kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)YesNo
Anonymous ID backupUserDefaultsNoNo
IDFVSystemYes (same vendor)No
Vendor IDsUserDefaultsNoNo
Session numberUserDefaultsNoNo

The Keychain recovery chain: Keychain → UserDefaults backup → generate new.

Android

DataStorageSurvives reinstallSurvives factory reset
Anonymous IDSharedPreferencesNoNo
ANDROID_IDSystem (per-signing-key since Android 8.0)YesNo
Vendor IDsSharedPreferencesNoNo
Session numberSharedPreferencesNoNo
⚠️

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:

VendorID nameFormatExample
GA4ga_client_id{random}.{timestamp}7234856102.1712498234
Metafbpfb.1.{timestamp_ms}.{random}fb.1.1712498234567.8765432109
TikTokttp27-char base36abcdefghijklmnopqrstuvwxyz1
Pinterestpin_unauthUUID v46f4f9e2a-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:

  1. Generates a new anonymous ID
  2. Clears user ID and traits
  3. Clears group ID and traits
  4. Regenerates vendor IDs
  5. Does not clear the event queue (pending events are still delivered)