Setting a User ID and Hashed PII
Send your internal user ID to Singular to enable cross-device tracking and user-level data reporting.
Note: If you use Singular's Cross-Device solution , you must collect the User ID across all platforms.
User ID Requirements
Privacy and Best Practices
Follow these guidelines when implementing user ID tracking to ensure privacy compliance and proper cross-device measurement.
- No PII: The User ID should not expose Personally Identifiable Information (PII) such as email addresses, usernames, or phone numbers. Use a hashed value unique to your first-party data. To send an email address or phone number, use the dedicated User PII API instead.
- Consistency Across Platforms: The User ID value must be the same internal identifier you capture across all platforms (Web/Mobile/PC/Console/Offline) for accurate cross-device measurement.
- First-Party Data: Singular includes the User ID in user-level exports, ETL, and Internal BI postbacks (if configured). The User ID is first-party data and is not shared with third parties.
-
Persistence:
The User ID persists until explicitly
unset using
unsetCustomUserId()or until the app is uninstalled. Closing or restarting the app does not clear the User ID.
Implementation Overview
When to Set the User ID
Use
setCustomUserId()
to set the user identifier and
unsetCustomUserId()
to clear it during logout.
Best Practice:
If multiple users share a single
device, implement a logout flow that calls
setCustomUserId()
on login and
unsetCustomUserId()
on logout.
If you already know the user ID when the app opens, call
setCustomUserId()
before initializing the Singular SDK.
This ensures Singular receives the User ID from the first session. However,
the User ID is typically unavailable until the user registers or logs
in, in which case call
setCustomUserId()
after the registration
or authentication flow completes.
SDK Methods
Set Custom User ID
Send your internal user ID to Singular for cross-device tracking and user-level reporting.
// Set the user ID after login or registration
Singular.setCustomUserId("custom_user_id")
// Set the user ID after login or registration
[Singular setCustomUserId:@"custom_user_id"];
Method Signature:
+ (void)setCustomUserId:(NSString *)customUserId;
+setCustomUserId:
returns
void
— there is no success/failure signal at the call site. The User ID is
attached to the next session and event the SDK sends.
The similarly-named
+setDeviceCustomUserId:
is annotated
__attribute__((deprecated))
and exists only for legacy compatibility. Always call
+setCustomUserId:
in new code.
Unset Custom User ID
Clear the user ID when a user logs out to ensure accurate session tracking for multi-user devices.
// Unset the user ID on logout
Singular.unsetCustomUserId()
// Unset the user ID on logout
[Singular unsetCustomUserId];
Method Signature:
+ (void)unsetCustomUserId;
User PII (Email and Phone)
The User ID above is your own opaque identifier. If you also want to send
the user's email address or phone number to Singular, use the separate
SingularUserDetails
API. The SDK normalizes and SHA-256 hashes these values on the device, so
the raw email address and phone number are never transmitted.
Availability: iOS SDK version 12.14.0 and above. This API is not available in the Kids SDK variant.
Choosing a Hashing Mode
There are two ways to supply PII. Pick one per user and stay consistent.
- SDK-hashed (recommended): Pass the cleartext email or phone number. The SDK normalizes the value, hashes it, and generates every variant Singular can match on.
- Pre-hashed: Pass values you have already normalized and SHA-256 hashed yourself. The SDK stores them exactly as given and does no further processing. Use this when your app is not permitted to hold cleartext PII at the point of the call.
Important: If you set both a cleartext value and its matching pre-hashed variant, the pre-hashed value wins.
SingularUserDetails Setters
| Setter | Details |
|---|---|
setEmail
|
Mode: SDK-hashed
Cleartext email address. The SDK trims whitespace and lowercases the
value before hashing. For
Example:
|
setPhoneNumber
|
Mode: SDK-hashed
Cleartext phone number. The SDK generates two variants: an E.164 form
that keeps a leading
Example:
|
setEmailSTD
|
Mode: Pre-hashed SHA-256 hash of the email address after trimming and lowercasing. |
setEmailNoDots
|
Mode: Pre-hashed
SHA-256 hash of the email address after trimming, lowercasing, and
removing any
|
setPhoneE164
|
Mode: Pre-hashed
SHA-256 hash of the phone number in E.164 form, keeping the leading
|
setPhoneDigits
|
Mode: Pre-hashed
SHA-256 hash of the phone number with every non-digit removed,
including the leading
|
Every setter has a matching getter (
getEmail
,
getPhoneNumber
,
getEmailSTD
,
getEmailNoDots
,
getPhoneE164
,
getPhoneDigits
) that returns the value you set.
Set PII at Initialization
Assign the object to
SingularConfig.userDetails
before calling
start
so the PII is attached to the first session the SDK sends.
let config = SingularConfig(apiKey: "SDK KEY", andSecret: "SDK SECRET")
let userDetails = SingularUserDetails()
userDetails.setEmail("user@example.com")
userDetails.setPhoneNumber("+15551234567")
config.userDetails = userDetails
Singular.start(config)
SingularConfig *config = [[SingularConfig alloc] initWithApiKey:@"SDK KEY"
andSecret:@"SDK SECRET"];
SingularUserDetails *userDetails = [SingularUserDetails new];
[userDetails setEmail:@"user@example.com"];
[userDetails setPhoneNumber:@"+15551234567"];
config.userDetails = userDetails;
[Singular start:config];
Set PII After Initialization
If the email address or phone number is only known after login or
registration, call
setUserDetails
at that point instead. The values are attached to every session and event
the SDK sends from then on.
// Cleartext values, hashed by the SDK
let userDetails = SingularUserDetails()
userDetails.setEmail("user@example.com")
userDetails.setPhoneNumber("+15551234567")
Singular.setUserDetails(userDetails)
// Or supply your own SHA-256 hashes
let hashed = SingularUserDetails()
hashed.setEmailSTD("b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514")
hashed.setPhoneE164("8a59780bb8cd2ba022bfa5ba2ea3b6e07af17a7d8b30c1f9b3390e36f69019e4")
Singular.setUserDetails(hashed)
// Cleartext values, hashed by the SDK
SingularUserDetails *userDetails = [SingularUserDetails new];
[userDetails setEmail:@"user@example.com"];
[userDetails setPhoneNumber:@"+15551234567"];
[Singular setUserDetails:userDetails];
// Or supply your own SHA-256 hashes
SingularUserDetails *hashed = [SingularUserDetails new];
[hashed setEmailSTD:@"b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"];
[hashed setPhoneE164:@"8a59780bb8cd2ba022bfa5ba2ea3b6e07af17a7d8b30c1f9b3390e36f69019e4"];
[Singular setUserDetails:hashed];
Method Signature:
+ (void)setUserDetails:(SingularUserDetails *)userDetails;
Clear PII
Stored PII persists in the iOS Keychain across app launches. Call
clearUserDetails
on logout, or whenever the user withdraws consent, to remove it.
// Remove stored PII on logout
Singular.clearUserDetails()
// Remove stored PII on logout
[Singular clearUserDetails];
Method Signature:
+ (void)clearUserDetails;
Note:
Because the values are persisted, assigning
SingularConfig.userDetails
on a later launch does not erase what was stored earlier.
clearUserDetails
is the only way to remove it.
Validation and Privacy Behavior
-
Email validation:
A cleartext email must contain exactly one
@and a dot after it. Invalid values are rejected and logged, not sent. - Phone validation: A cleartext phone number must contain at least 6 digits. Shorter values are rejected and logged.
-
Wrong-mode protection:
A value that already looks hashed is rejected by
setEmailandsetPhoneNumber. Likewise, the pre-hashed setters reject anything that is not a 64-character SHA-256 hex string. - Limit Data Sharing: While Limit Data Sharing is enabled, the PII payload is withheld from all requests. See Data Privacy .
- Consent: Collect and send email addresses and phone numbers only where you have a lawful basis to do so. Hashing does not remove your obligations under GDPR, CCPA, or equivalent regulations.