Generate a UUID on Android (and iOS) to Identify an App Install

A common requirement in mobile apps is a stable ID for "this installation of the app on this device": to tie analytics events together, to register for push notifications, or to let a backend tell clients apart before anyone logs in. The usual answer is to generate a random UUID on first launch and store it. That works, but where you store it decides when the ID survives and when it does not, and on Android the default backup behaviour can quietly copy one ID to two phones. This article covers the code and the platform rules on Android and iOS.

Generating the UUID

Both platforms have a built-in generator for random, version 4 UUIDs, so no library is needed. On Android, Kotlin and Java use java.util.UUID:

import java.util.UUID

val id: String = UUID.randomUUID().toString()
// e.g. fd92f37e-fdb4-423d-9b4d-755f7d66d2e6  (36 characters, version 4)

randomUUID() takes its 122 random bits from a cryptographically strong generator, so collisions between installs are not a practical concern even across hundreds of millions of devices. On iOS the equivalent is UUID().uuidString in Swift, which returns the same 36-character format in upper case. Normalise case on the server if both platforms send IDs to the same table.

The hard part is not generation but persistence: the ID must be created exactly once and then read back on every launch.

Storing it on Android

For a value this small, SharedPreferences or Jetpack DataStore is enough. Generate on first access and keep it:

object InstallId {
    private const val PREFS = "install_id"
    private const val KEY = "id"

    @Synchronized
    fun get(context: Context): String {
        val prefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
        prefs.getString(KEY, null)?.let { return it }
        val id = UUID.randomUUID().toString()
        prefs.edit().putString(KEY, id).apply()
        return id
    }
}

The ID lives as long as the app's data. It survives app updates and reboots, and it is lost when the user uninstalls the app or clears its storage in Settings. For an "installation ID" that is the correct behaviour: a reinstall is a new installation. If you need an ID that survives reinstalls, what you actually need is an account, because Android deliberately offers no per-device identifier that apps can read without restrictions.

The Auto Backup trap: one ID on two phones

Since Android 6.0, Auto Backup saves an app's files to the user's Google Drive, and shared preferences are included by default. Android's documentation says the data is restored whenever the app is installed: from the Play Store, during setup of a new phone, or by adb install. So when a user sets up a new phone from a backup, the new phone gets the old phone's install ID, and if both phones stay in use, your backend sees one client that is in two places at once. Device-to-device transfers on Android 12 and later can copy it the same way.

Exclude the file that holds the ID from both backup mechanisms. Android 12 and later read android:dataExtractionRules; Android 11 and earlier read android:fullBackupContent, so a complete app declares both:

<!-- AndroidManifest.xml -->
<application
    android:dataExtractionRules="@xml/data_extraction_rules"
    android:fullBackupContent="@xml/backup_rules" ... >

<!-- res/xml/data_extraction_rules.xml (Android 12+) -->
<data-extraction-rules>
    <cloud-backup><exclude domain="sharedpref" path="install_id.xml"/></cloud-backup>
    <device-transfer><exclude domain="sharedpref" path="install_id.xml"/></device-transfer>
</data-extraction-rules>

<!-- res/xml/backup_rules.xml (Android 11 and lower) -->
<full-backup-content>
    <exclude domain="sharedpref" path="install_id.xml"/>
</full-backup-content>

If restoring the ID is what you want, for example to keep a user's anonymous preferences on a new phone, leave it in the backup, but then treat the value as "this user's data" rather than "this device" on the server.

Why not ANDROID_ID?

Settings.Secure.ANDROID_ID looks like a ready-made device ID, and it is readable without a permission. Since Android 8.0 it is a 64-bit value unique to each combination of app-signing key, user and device. That has consequences worth knowing before you rely on it:

val androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
// 16 hex characters; same for every app signed with your key, for this user, on this device

It survives uninstall and reinstall, which a generated UUID does not, but it changes after a factory reset or if your signing key changes, and every work profile or secondary user gets a different value. It is shared by all apps you sign with the same key, which is useful for linking your own apps and a privacy concern for the same reason. Google's guidance on user data IDs recommends app-scoped identifiers, a generated ID or the Firebase installation ID, for most purposes, and the Advertising ID only for advertising. Use ANDROID_ID only if you genuinely need an ID that survives reinstalls and can live with its scope.

The same task on iOS

iOS offers two built-in options and one stored one. UIDevice.current.identifierForVendor is the same for all of your apps on one device; Apple's documentation says it changes when the user deletes all of your apps and later reinstalls one, and it can be nil right after a restart before the user unlocks the device. A UUID in UserDefaults behaves like SharedPreferences: it is removed with the app. A UUID in the Keychain lasts longer, because in practice Keychain items stay on the device after the app is deleted, although Apple does not document that as guaranteed.

// Swift: a per-install ID in UserDefaults
let defaults = UserDefaults.standard
let installId = defaults.string(forKey: "install_id") ?? {
    let id = UUID().uuidString
    defaults.set(id, forKey: "install_id")
    return id
}()

If you store the ID in the Keychain, choose an accessibility class ending in ThisDeviceOnly, such as kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly. Apple's documentation states that such items do not migrate to a new device, which avoids the iOS version of the backup problem. Without it, a restore onto a new iPhone can bring the old ID along.

Designing the server side

Treat the install ID as a client identifier, not as authentication. Anyone can read it from their own device and send a different one, so never grant access based on it alone. Register it together with something the server issues, such as a session token, and expect it to change: on reinstall, data clearing, restore with backups excluded, or a new device.

Mention the ID in your privacy policy if you send it to a server. A random per-install UUID is far less sensitive than a hardware ID, but both Apple's privacy labels and Google Play's Data safety form ask you to declare identifiers your app collects. For test fixtures and seed data, a UUID generator produces valid version 4 values in bulk.

Sources and further reading

The Android reference for Settings.Secure.ANDROID_ID describes its scoping since Android 8.0. Android's "Back up user data with Auto Backup" guide covers what is backed up, when data is restored and the dataExtractionRules and fullBackupContent formats, and "Best practices for unique identifiers" compares the ID options. Apple's documentation for identifierForVendor and kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly gives the iOS rules. RFC 9562 defines version 4 UUIDs.

Frequently asked questions

How do I generate a UUID in Android?

Call UUID.randomUUID().toString() from java.util.UUID in Kotlin or Java. It returns a random version 4 UUID such as fd92f37e-fdb4-423d-9b4d-755f7d66d2e6, generated with a cryptographically strong random source.

Can I store a UUID to identify an app client on Android and iOS?

Yes. Generate it on first launch and save it in SharedPreferences or DataStore on Android and in UserDefaults or the Keychain on iOS. It identifies one installation until the app is removed or its data is cleared.

Does a generated UUID survive reinstalling the app?

On Android, not unless Auto Backup restores it, which it does by default. On iOS, a UUID in UserDefaults is lost, while one in the Keychain usually remains on the device.

Should I use ANDROID_ID instead of a UUID?

Only if you need an ID that survives reinstalls. Since Android 8.0 it is scoped to your signing key, the user and the device, and it resets after a factory reset. For most uses an app-generated UUID is the recommended choice.

Why do two devices send the same install UUID?

Android Auto Backup restored the old phone's shared preferences on the new one. Exclude the preferences file holding the ID with dataExtractionRules and fullBackupContent backup rules.

What is the iOS equivalent of ANDROID_ID?

identifierForVendor, which is shared by all apps from the same vendor on a device. It changes when the user deletes all of that vendor's apps and reinstalls one, and it can be nil before the first unlock after a restart.