Customizing device identifiers on iOS
PingOne Advanced Identity Cloud PingAM 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 Learn more in [configure]. |
To retrieve a device identifier on iOS, instantiate DefaultDeviceIdentifier and access the id property:
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.
DefaultDeviceIdentifier generates an identifierimport 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:
// 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:
UUIDDeviceIdentifier for identifiersimport 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 Learn more in [custom] |
== Creating custom device identifiers
For advanced use cases, you can create generate custom identifiers by implementing the DeviceIdentifier protocol:
DeviceIdentifier protocolimport 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
}
}