ATT and IDFA (iOS 14+)
On iOS 14+, the IDFA isn’t collected automatically — Apple requires explicit user permission via App Tracking Transparency (ATT). After receiving the user’s choice, pass the status to the SDK so it can attach att_authorization_status (and optionally advertising_id) to event payloads.
Option 1 — SDK reads IDFA from the device
import AppTrackingTransparency
import AdSupport
import ZeotapCollect
ATTrackingManager.requestTrackingAuthorization { status in
Collect.getInstance()?.setAdvertisingIdByATTStatus(status: status)
}
The SDK reads the IDFA via ASIdentifierManager when status is .authorized. For other statuses, only att_authorization_status is attached.
Option 2 — explicit status + IDFA
Useful when your app already has the IDFA in hand:
ATTrackingManager.requestTrackingAuthorization { status in
switch status {
case .authorized:
Collect.getInstance()?.setATTStatusAndAdvertisingId(
status: status,
advertisingId: ASIdentifierManager.shared().advertisingIdentifier.uuidString
)
case .denied, .notDetermined, .restricted:
Collect.getInstance()?.setATTStatusAndAdvertisingId(status: status)
@unknown default:
break
}
}
For iOS 13 and below the SDK collects IDFA automatically — no ATT call needed.
att_authorization_status values match Apple’s enum: authorized, denied, not_determined, restricted. See Apple’s ATT documentation.
Identity patterns
Tie post-login behavior to a known user
When the user logs in:
Collect.getInstance()?.setUserIdentities([
"email": "user@example.com",
"loginid": "userA-123",
"fpuid": "crm_42"
])
Collect.getInstance()?.setEventProperties("login", ["method": "password"])
All subsequent events carry those identities.
Merge unknown and known users
Use setUserProperties (not setUserIdentities) to attach additional attributes to the same zi:
Collect.getInstance()?.setUserProperties([
"plan": "premium",
"country": "DEU"
])
Differentiate two users on the same device
setUserIdentities ties events to the current identity until you unset:
Collect.getInstance()?.setUserIdentities(["email": "alice@example.com"])
// ... Alice's activity
Collect.getInstance()?.unsetUserIdentities()
Collect.getInstance()?.setUserIdentities(["email": "bob@example.com"])
// ... Bob's activity
Send identities only after the SDK hashes them
let opts = CollectOption()
.writeKey(value: "YOUR_WRITE_KEY")
.areIdentitiesHashed(value: false)
.hashIdentities(value: true)
.build()
Collect.getInstance()?.setUserIdentities([
"email": "xyz@gmail.com", // hashed by SDK
"crmID": "12345" // custom identity, sent as-is
])
Send identities that are already hashed
let opts = CollectOption()
.writeKey(value: "YOUR_WRITE_KEY")
.areIdentitiesHashed(value: true)
.build()
Collect.getInstance()?.setUserIdentities([
"email_sha256_lowercase": "32e19a491662fd86de7d3806b1199b76f0ee44e928d3475f05b0c8a59912c097",
"crmID": "12345"
])
Common payloads
Add to cart
Collect.getInstance()?.setEventProperties("addToCart", [
"productID": "sku_123",
"quantity": 1,
"price": 29.99
])
Purchase
Collect.getInstance()?.setEventProperties("purchase", [
"orderID": "order_456",
"total": 89.97,
"currency": "EUR"
])
Logout
Collect.getInstance()?.unsetUserIdentities()
Set events with a callback
All event methods accept an optional ResponseCallback. Use it to confirm dispatch or surface errors in dev:
Collect.getInstance()?.setEventProperties("Add_to_cart") { data in
print("Status: \(data.status), Message: \(data.message)")
}
See SDK Reference for the callback contract.
Working with zi
The SDK generates a zi per app install per write key. Override or reset it for advanced flows:
// Get current zi
let zi = Collect.getInstance()?.getZI()
// Reset zi (e.g. on logout when you want a fresh identity)
Collect.getInstance()?.resetZI()
// Override zi (e.g. use your own hashed loginid)
Collect.getInstance()?.setZI(zi: "your-hashed-loginid")
Pause and resume collection
Useful for screens where you don’t want background events:
Collect.getInstance()?.pauseCollection()
// ... no events sent in this window
Collect.getInstance()?.resumeCollection()
Contextual data Zeotap captures automatically
You don’t need to send these explicitly — the SDK appends them to every event:
app_name, app_version
device_model_identifier, os_type, os_version
advertising_id (with ATT-derived att_authorization_status on iOS 14+)
network_type, carrier
country (IP-derived or from userCountry override)
zi (SDK-generated user ID)
Last modified on June 22, 2026