When Apple introduced App Tracking Transparency in iOS 14.5, most users started declining the IDFA prompt and ad-campaign measurement for iOS apps quietly broke. The replacement is AdServices.framework, a small system API that produces a privacy-preserving attribution token. The token tells you whether a user came from an Apple Search Ads campaign, plus campaign metadata, without identifying the user. No ATT prompt required.
In a hybrid app I worked on, the integration looked simple in theory and had three details that mattered in practice.
The token isn’t ready immediately
Calling AAAttribution.attributionToken() right after install can return a “platform not supported yet” error. Apple’s docs recommend retrying with backoff. Our setup made three attempts at five-second intervals, then gave up. After that, the token is durable: the same token can be exchanged for an attribution record on Apple’s server later. The token-to-record exchange is an HTTPS POST to api-adservices.apple.com.
What I’d build differently now
If I built this again, I’d push the responsibility one step downstream: have the client send just the attribution token to our own backend, and let the backend exchange it with Apple for the attribution record.
Two reasons. A backend has a more reliable connection than a phone in a tunnel, so the retry-with-backoff and the reachability gating become almost trivial. And the logic centralises: the same endpoint works for any client (web, Android, iOS, whatever’s next), and the analytics enrichment all happens server-side.
The client’s job collapses to: fetch the token from AdServices, POST it once to our own API, forget about it. Apple’s api-adservices.apple.com endpoint exits the iOS codebase entirely.