---
title: Customizing device identifiers on iOS
description: PingOne Advanced Identity Cloud PingAM iOS
component: orchsdks
page_id: orchsdks:journey:use-cases/device-profiling/ios-device-ids
canonical_url: https://developer.pingidentity.com/orchsdks/journey/use-cases/device-profiling/ios-device-ids.html
keywords: ["Device", "Hardware", "Source Code", "Integration", "SDK", "iOS"]
---

# Customizing device identifiers on iOS

[icon: circle-check, set=far]PingOne Advanced Identity Cloud [icon: circle-check, set=far]PingAM [icon: apple, set=fab]iOS

The **Device Profiling** module uses the **Device ID** module to generate a unique identifier for the device profile.

The **Device ID** module provides utilities for generating and retrieving unique device identifiers.

The default keychain-based mechanism for generating device identifiers exhibits the following behavior:

| Scenario                        | Behavior                                                                                                                                                                                                          |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **App Uninstall and Reinstall** | The identifier **persists**.The keychain is not cleared when an app is deleted, so the new installation can access the same key.                                                                                  |
| **App Data Cleared**            | This is not a standard user action on iOS.Uninstalling and reinstalling the app is the closest equivalent, as outlined above.                                                                                     |
| **Device Backup and Restore**   | The identifier **persists** if the device is restored from an encrypted iCloud or local backup, as these backups include keychain data.                                                                           |
| **Factory Reset**               | The identifier is **permanently deleted** as the entirety of device storage, including the keychain, is cleared .                                                                                                 |
| **Sharing Across Apps**         | The identifier can be shared across apps from the same developer by using the same **Keychain Access Group** using the `keychainAccount` property in the configuration.Learn more in [\[configure\]](#configure). |

To retrieve a device identifier on iOS, instantiate `DefaultDeviceIdentifier` and access the `id` property:

```swift
import PingDeviceId

let deviceIdentifier = try DefaultDeviceIdentifier()

// Access the identifier in an async context
Task {
    do {
        let id = try await deviceIdentifier.id
        print("Device ID: \(id)")
    } catch {
        print("Error retrieving device ID: \(error)")
    }
}
```

The module generates the ID on its first use and subsequently retrieves it from the cache or the Keychain.

The ID is a SHA-256 hash of the public key, providing a stable and unique representation of the device.

\=== Configuring the default identifier generation

Depending on your requirements, you can configure how `DefaultDeviceIdentifier` generates an identifier by using the `DeviceIdentifierConfiguration` method.

For example, you can specify the keychain account name or disable encryption. You should not disable encryption in a production environment.

Customizing how `DefaultDeviceIdentifier` generates an identifier

```swift
import PingDeviceId

// Define a custom configuration
let config = DeviceIdentifierConfiguration(
    keychainAccount: "com.mycompany.myapp.deviceid",
    useEncryption: true,
    keySize: 2048
)

do {
    let deviceIdentifier = try DefaultDeviceIdentifier(configuration: config)
    let id = try await deviceIdentifier.id
    print("Custom Device ID: \(id)")
} catch {
    // Handle potential initialization or retrieval errors
}
```

\=== Regenerating the Identifier

You can explicitly delete the existing keypair from the keychain and generate a new one, by using the `regenerateIdentifier()` method:

Replacing an existing keypair

```swift
// In an async context
do {
    let newId = try await deviceIdentifier.regenerateIdentifier()
    print("New Device ID: \(newId)")
} catch {
    print("Error regenerating ID: \(error)")
}
```

\== Falling back to a simple `UUIDDeviceIdentifier`

Depending on your requirements, or the types of device you will be supporting, you may require a simpler, but less secure device identifier. For example, if using the keychain results in errors on certain devices.

In these cases, you can fall back to using `UUIDDeviceIdentifier` to create identifiers:

Using the simpler `UUIDDeviceIdentifier` for identifiers

```swift
import PingDeviceId

let deviceIdentifier = try UUIDDeviceIdentifier()

// Access the identifier in an async context
Task {
    do {
        let id = try await deviceIdentifier.id
        print("Device ID: \(id)")
    } catch {
        print("Error retrieving device ID: \(error)")
    }
}
```

|   |                                                                                                                                                                                               |
| - | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | You can also create your own custom identifiers if the provided `DefaultDeviceIdentifier` or `UUIDDeviceIdentifier` methods do not meet your requirements.Learn more in [\[custom\]](#custom) |

\== Creating custom device identifiers

For advanced use cases, you can create generate custom identifiers by implementing the `DeviceIdentifier` protocol:

Implementing custom identifiers using the `DeviceIdentifier` protocol

```swift
import PingDeviceId

public protocol DeviceIdentifier: Sendable {
    var id: String { get async throws }
}

// Example: A custom identifier that uses UUID
struct CustomDeviceIdentifier: DeviceIdentifier {
    var id: String {
        // This is a simple example; for persistence, you would need
        // to save and retrieve the UUID from a storage mechanism.
        return UUID().uuidString
    }
}
```
