There is no single universal “device ID” that every mobile app can read. iOS and Android provide several identifiers, each designed for a particular purpose and scope. The correct answer depends on whether you need an app-install ID, an identifier shared across your own apps, an advertising ID, or a support value visible to a user.
The safest rule is simple: choose the narrowest resettable identifier that solves the stated use case. Do not collect IMEI, serial number, MAC address, or another hardware identifier for ordinary analytics or attribution.
Quick answer: which identifier should you use?
| Use case | iOS choice | Android choice | Important limit |
|---|---|---|---|
| Identify one app installation | App-generated UUID | App-generated UUID or Firebase Installation ID | Resets when app data is removed or the app is reinstalled, depending on storage |
| Analytics across your own apps | IDFV | App Set ID | Must stay inside the permitted first-party scope |
| Advertising and cross-company measurement | IDFA after ATT authorization | Google Advertising ID when available | Respect user choice, platform policy, disclosures, and applicable law |
| Diagnose a support case | App-generated support ID | App-generated support ID | Prefer a value that reveals no platform advertising identifier |
| Verify app authenticity or abuse | App Attest or DeviceCheck where appropriate | Play Integrity API | These are attestation signals, not general-purpose tracking IDs |
If the purpose is “count active installations,” an app-instance ID is usually enough. If the purpose is “connect activity after sign-in,” use your own account ID after authentication. If the purpose is advertising, use the platform advertising framework and handle unavailable states without bypassing the user’s choice.
What a device identifier actually identifies
Before retrieving any ID, document four properties:
- Scope: one session, one installation, one app, one developer’s app set, or advertising across eligible apps.
- Lifetime: when the value can reset or change.
- Availability: permissions, services, device state, and platform version required.
- Purpose: analytics, fraud prevention, advertising, account functionality, or support.
An identifier is not necessarily a person. One person may use several devices, several profiles on one device, or reinstall an app. A family may share one device. An advertising ID can reset. Treating any device-scoped value as a permanent customer key creates duplicate and merged profiles.
iOS identifiers
IDFV: Identifier for Vendor
Apple’s identifierForVendor returns a UUID that identifies a device to the app’s vendor. Apps from the same vendor on the same device normally receive the same value. Apple says the value can change after the user removes all apps from that vendor and later reinstalls one; it can also differ for some development or ad-hoc installations. Immediately after a restart, the value may be nil until the user unlocks the device.
Retrieve it in Swift:
import UIKit
func currentVendorIdentifier() -> String? {
UIDevice.current.identifierForVendor?.uuidString
}Use IDFV for permitted first-party analytics across your own apps. Apple states that ATT is not required merely for this first-party analytics use, but IDFV must not be combined with other data to track someone across apps or websites owned by other companies.
Do not make IDFV your database primary key. Store it as a changeable signal and use an authenticated account ID for account continuity.
IDFA: Identifier for Advertisers
IDFA is available through AdSupport for advertising-related use. On iOS 14.5 and later, an app must use AppTrackingTransparency when its activity meets Apple’s definition of tracking or it wants access to the advertising identifier. Without authorization, the advertising identifier is all zeros and the app may not substitute another identifier to continue the prohibited tracking.
Check the authorization state before reading the value:
import AdSupport
import AppTrackingTransparency
func authorizedAdvertisingIdentifier() -> String? {
guard ATTrackingManager.trackingAuthorizationStatus == .authorized else {
return nil
}
let value = ASIdentifierManager.shared().advertisingIdentifier
let zero = "00000000-0000-0000-0000-000000000000"
return value.uuidString == zero ? nil : value.uuidString
}Request ATT only at a moment where the purpose is clear. Add NSUserTrackingUsageDescription, explain the use accurately, and ensure no tracking starts before authorization. Do not gate unrelated app functionality or reward a user for accepting the system prompt.
App-generated installation ID
For installation-scoped analytics or support, generate a random UUID and store it in normal app storage:
import Foundation
let installationID = UUID().uuidStringPersist it in UserDefaults or an app database when reset-on-reinstall behavior is desired. Keychain persistence can survive reinstall in some circumstances and changes the privacy and lifecycle expectation, so do not use Keychain merely to rebuild a deleted identity.
Android identifiers
App-generated UUID or Firebase Installation ID
Google recommends a Firebase Installation ID or a privately stored GUID for most non-advertising use cases. A local GUID can be created in Kotlin:
import java.util.UUID
val installationId = UUID.randomUUID().toString()Generate it once, persist it in app-private storage, and rotate it when the user invokes a reset or deletion control. Clearing app data or reinstalling should create a new installation identity unless your clearly documented product requirement says otherwise.
App Set ID
App Set ID supports analytics or fraud-prevention use cases across apps owned by the same Google Play developer account. Play-installed apps normally receive developer scope; apps installed another way or on devices without suitable Google Play services may receive app scope.
The value can reset when the last app in the set is uninstalled, after 13 months without access by the app set, after a factory reset, or after some SDK scope changes. Google says to retrieve it when needed rather than rely on a permanently cached value.
val client = AppSet.getClient(applicationContext)
client.appSetIdInfo.addOnSuccessListener { info ->
val id = info.id
val scope = info.scope
// Send only if this use is covered by your documented purpose.
}App Set ID is not an advertising identifier. Google’s guidance limits it to non-advertising purposes such as first-party analytics and fraud prevention.
ANDROID_ID
On Android 8.0 and later, Settings.Secure.ANDROID_ID is a 64-bit value represented as a hexadecimal string and scoped to the combination of app-signing key, user, and device. It can change after a factory reset or signing-key change. Multiple Android users on the same physical device receive different values.
import android.provider.Settings
val androidId = Settings.Secure.getString(
contentResolver,
Settings.Secure.ANDROID_ID
)ANDROID_ID is not the right substitute for an unavailable advertising ID. Use it only when its scope and lifetime fit a non-advertising purpose and your disclosures cover the use.
Google Advertising ID
Google’s advertising ID is user-resettable and user-deletable. Google Play policy requires apps to use it, when available, instead of other device identifiers for advertising purposes. When a user deletes it, attempts to access it return zeros. Apps targeting Android 13 or later must declare the com.google.android.gms.permission.AD_ID permission when they use the Google Play services advertising ID; otherwise the returned value is zeroed.
Retrieval is asynchronous and can fail when Google Play services is missing or unavailable. Do it off the main thread and treat every unavailable, deleted, zero, or limited state as normal:
val info = AdvertisingIdClient.getAdvertisingIdInfo(applicationContext)
val advertisingId = info.id
val limited = info.isLimitAdTrackingEnabledDo not bridge a reset by linking the new advertising ID to the old one through a persistent identifier or fingerprint without the explicit permission required by policy.

How users can find a support identifier
End users normally cannot see IDFV, App Set ID, or an app’s private installation UUID in system settings. If support needs a stable value, add an in-app screen such as Settings → Help → Diagnostic ID.
A good diagnostic ID should:
- Be generated by your app
- Be safe to copy into a support ticket
- Avoid exposing IDFA, Google Advertising ID, account tokens, email, or hardware identifiers
- Be rotatable through a privacy or reset control
- Map server-side to a limited diagnostic record with short retention
For Android’s advertising ID, the settings path varies by manufacturer and OS version. Google documents a common route under Settings → Privacy → Ads, where users can reset or delete the advertising ID. Do not instruct users to install an unknown “device ID” utility.
Identifier formats are not identity guarantees
Many values use UUID notation such as 38400000-8cf0-11bd-b23e-10b96e40000d; ANDROID_ID is typically a hexadecimal representation of a 64-bit number. Format validation only proves the string resembles an expected value. It does not prove:
- The value belongs to a unique person
- It has not reset
- It is authorized for your use
- It came from a genuine device
- It can be shared with another company
Validate source, scope, permission state, and timestamps alongside syntax.
A safe identifier data model
Do not put every signal into a single device_id column. Use explicit fields:
| Field | Example purpose |
|---|---|
installation_id | One app installation |
account_id | Signed-in product identity |
identifier_type | IDFV, App Set ID, advertising ID, or none |
identifier_scope | Install, vendor, developer, or advertising |
authorization_state | Authorized, denied, restricted, not determined, unavailable |
observed_at | When the app read the value |
resettable | Whether the platform/user can rotate it |
source_platform | iOS or Android |
Store only what you need. Limit access, encrypt identifiers in transit and at rest, define retention, and propagate deletion requests to processors. Hashing an identifier does not automatically make it anonymous; a stable hash can still be linkable.
Common mistakes
- Using IMEI or serial number for analytics. Modern platforms restrict hardware identifiers, and ordinary apps do not need them.
- Calling IDFA before ATT authorization. Handle all four ATT states and accept
nilor zeros. - Replacing a deleted advertising ID with ANDROID_ID. That defeats the user control and can violate Play policy.
- Assuming reinstall preserves identity. Model reinstall and reset as expected lifecycle events.
- Calling advertising ID APIs on the main thread. Retrieval can block or fail.
- Persisting IDs forever. Retention should follow the business purpose and user controls.
- Calling a probabilistic match a device ID. A confidence-based attribution inference is not a stable identifier and should never be presented as deterministic identity.
That last one is worth checking in your measurement vendor, not just your own code. Deeplinkly attributes from deterministic signals — Play Install Referrer, Meta Install Referrer, and SKAdNetwork postbacks — and reports an install as unattributed when none is present, rather than fingerprinting the device to manufacture a match. If a vendor's attributed total is suspiciously close to your install total, ask which of the two it is doing.
Final decision checklist
- Write the exact purpose before selecting an identifier.
- Prefer account, installation, vendor, or developer scope over cross-app scope.
- Implement unavailable and reset states as normal paths.
- Do not bridge resets or denied permission with a hidden identifier.
- Keep advertising identifiers out of logs, crash messages, and support screens.
- Review every third-party SDK’s collection and disclosure behavior.
- Test reinstall, data clearing, account change, ATT denial, ad-ID deletion, and devices without Google Play services.
The right way to “know a device ID” is not to find the longest-lived value. It is to choose the smallest, most transparent identifier that performs one legitimate job and can change when the platform or user says it should.
Primary sources
- Apple: identifierForVendor
- Apple: AppTrackingTransparency
- Apple: User privacy and data use
- Android: Best practices for unique identifiers
- Android: App Set ID
- Android: ANDROID_ID reference
- Google Play: Advertising ID
- Android: Advertising ID permission on Android 13