User Identification
Resolution order¶
When an event is processed, the SDK picks the user identifier from the first available source:
- Per-event override — the
customUserIDparameter onTelemetryDeck.event(...) - Explicit identifier — set at runtime via
TelemetryDeck.setUserIdentifier(_:) - Default identifier — from the
defaultUserinitialization parameter, or auto-resolved from the platform
If you never call setUserIdentifier and don't pass defaultUser at init, the SDK resolves a default automatically.
Platform defaults¶
| Platform | Source |
|---|---|
| iOS, tvOS, visionOS | UIDevice.identifierForVendor |
| watchOS | WKInterfaceDevice.identifierForVendor |
| macOS | Random UUID, generated once and persisted in UserDefaults |
| Linux / server | Static string derived from platform and app version |
identifierForVendor (iOS, tvOS, visionOS, watchOS)¶
On Apple's mobile and wearable platforms, the SDK uses identifierForVendor (IDFV). This is a UUID assigned by the OS that:
- Stays stable across launches and app updates
- Is shared across all apps from the same vendor on a given device
- Resets when the user uninstalls all of that vendor's apps from the device
- Differs between TestFlight and the App Store, even for the same build — a tester's IDFV in TestFlight will not match their IDFV after installing from the App Store
See Apple's identifierForVendor documentation for the full lifecycle rules, including edge cases around app groups and enterprise distribution.
If identifierForVendor returns nil — which can happen before the device is first unlocked after a restart — the SDK falls back to a generic string based on platform and app version. Different users on the same app version temporarily share this identifier until IDFV becomes available. The identifier is resolved once at startup with no retry.
macOS¶
macOS has no identifierForVendor. On first launch, the SDK generates a random UUID and stores it in a UserDefaults suite scoped to your app ID. Subsequent launches read the same UUID. Deleting the app's UserDefaults data resets the identifier.
Linux and server-side Swift¶
The SDK returns a static string derived from the platform name and app version. Every user on the same deployment gets the same identifier. For server-side use, pass an explicit user identifier:
Setting a user identifier¶
Override the default at any point after initialization:
Pass nil to revert to the platform default:
For a single event:
Hashing¶
The raw identifier is SHA256-hashed at the end of the processor pipeline, just before the event enters the cache. The hash input is identifier + salt, where salt is the value you pass to initialize(appID:namespace:salt:) (empty string by default).
The result is a 64-character lowercase hex string. The unhashed identifier never leaves the device. The TelemetryDeck server applies an additional server-side salt, so even if two apps use the same user email with the same client salt, the final stored hashes differ between apps.
Recommendations¶
- Most apps: the platform default (IDFV) is sufficient. Users are tracked per-device, anonymously.
- Cross-device identity: if you want to correlate the same user across iPhone and iPad, call
setUserIdentifierwith a stable account identifier (email, user ID). It gets hashed before transmission. - Server-side Swift: always pass
customUserID— the default identifier is meaningless on servers. - Salt: adding a
saltat initialization provides an extra layer — even if someone intercepts the hashed identifier, they can't reverse it against a known-plaintext attack without knowing your salt.