Mobile Consent Management for Your Attribution Stack

Mobile consent management means gating your MMP SDK's data transmission behind two independent signals — ATT (device permission for IDFA) and GDPR/DMA consent (legal basis from the CMP) — and getting the firing order right. The CMP collects consent first. The SDK delays its send. Then the install event ships with both signals attached. Fire too early and you lose the IDFA on that install forever. Gate too hard and you silently kill attribution.

The install that vanished

A client shipped what they called their "compliance release." Clean CMP, TCF v2.2 string, the works. Twelve hours later their attributed installs on iOS were down roughly 40% against the prior week's baseline. No traffic drop. No spend change. Just installs falling out of the dashboard.

Fraud was my first instinct. It usually is. But the pattern was wrong for fraud — organics held steady, and the loss sat on paid iOS only.

The cause was boring and expensive. The MMP SDK initialized in application(_:didFinishLaunchingWithOptions:), before the CMP had written its consent string and before ATT had resolved. So the install event fired against an empty consent state and a zeroed IDFA. Every affected install permanently lost its device identifier. You can't backfill that. The window closes at first launch.

Call it 4,000 basis points of attributed installs gone overnight, on a media budget that didn't move. Nobody planned a single line for it. The fix was three lines of ordering.

Three systems everyone conflates

People say "consent" like it's one thing. It's three, and they're independent.

App Tracking Transparency (ATT) is a device-level permission from Apple. It gates access to the IDFA. That's it. A user who allows tracking gives you a readable device identifier. A user who denies it hands you a string of zeros. ATT says nothing about legal basis.

GDPR/DMA consent is a jurisdictional legal basis, collected through a CMP and encoded as a TCF consent string. This is the layer that decides whether you're allowed to process a user's data at all under EU law. Didomi's documentation is blunt about the relationship: "To be fully compliant with both Apple and GDPR requirements, you must ask for the user permission through ATT AND ask for user consent through the CMP." ATT is not a valid TCF consent mechanism. It cannot replace the CMP.

SDK init gating is the firing order — the sequencing that determines whether the install is measured at all. This is plumbing, not policy, and it's the part that broke my client.

Google made the ATT-is-not-consent point non-negotiable on its side too. Per Adjust's DMA writeup, "Google does not consider responses to an ATT prompt to directly reflect DMA consent values, and therefore they may not be used." Two separate signals. Two separate collection paths.

Here's the ordering that actually works. Read it as a sequence, top to bottom.

  1. App launches. No third-party SDK does anything yet. The MMP SDK is initialized but held — its send is deferred by a wait/delay API.
  2. CMP renders. The consent management platform collects GDPR/DMA consent from the user and writes the TCF string to on-device storage: NSUserDefaults on iOS, SharedPreferences on Android. AppsFlyer's documentation describes exactly this handoff — "A CMP compatible with TCF v2.2/2.3 collects DMA consent data and stores it in NSUserDefaults (iOS) and SharedPreferences (Android)."
  3. ATT prompt resolves (timed after onboarding, not at cold start). The IDFA becomes readable or zeroed depending on the response.
  4. MMP SDK reads both signals — TCF string from device storage, IDFA from the ATT result — and only then transmits the install event.

The rule underneath step 1 is the one consentmanager states plainly: "Our CMP SDK must collect users' consent prior to invoking any action whatsoever in the third-party SDKs." Consent first. Attribution SDK second. Never the reverse.

What makes step 4 wait for steps 2 and 3 is the MMP's delay API. AppsFlyer's waitForATTUserAuthorization "enables you to configure how long the SDK should defer and wait for ATT status before sending data to the AppsFlyer servers." The SDK queues launch and in-app events in memory, then flushes once the signals land.

If you want the full picture of where each of these pieces sits, we mapped it in the privacy-first attribution reference architecture.

Why the firing order is non-negotiable

The trap is mechanical, and it's symmetric.

Fire the SDK before ATT resolves, and the install event carries a zeroed IDFA. That identifier never comes back for that install. First-launch attribution is a one-shot event. Miss the window, and no amount of later opt-in recovers it.

Gate too aggressively — hold the send indefinitely, or block it on a consent state you never satisfy — and the install event never ships. Now you're compliant and blind. Attribution simply doesn't arrive.

The delay APIs exist to keep that window open just long enough. AppsFlyer's wait interval runs up to about 120 seconds. Adjust's documentation puts the onboarding delay in the same range: "The Adjust SDK enables you to delay the ATT prompt launch for up to 120 seconds to give your users time to complete the initial onboarding before informing you of their consent decision," with newer developer docs extending toward 360. That number isn't a performance knob. It's the width of the window where a user can finish onboarding, answer the CMP, answer ATT, and still have the install measured.

Pick a delay that fits your onboarding, not one you copied from a sample project. Too short cuts off slow onboarders. Too long risks users backgrounding the app before the send fires.

Here's the sequence I hand to engineering. Each step notes which signal it satisfies.

1. Initialize the CMP and collect consent before any third-party SDK action. This satisfies your GDPR/DMA legal basis. The MMP SDK can be constructed here, but it must not send. consentmanager's ordering rule governs this step directly.

2. Configure the MMP delay/wait interval. Set waitForATTUserAuthorization (AppsFlyer) or the equivalent Adjust wait interval so the SDK holds its events. This is the plumbing that keeps the IDFA window open.

3. Trigger the ATT prompt after onboarding, not at cold start. Adjust's up-to-120-second delay is built for exactly this — let the user get value first, then ask. This satisfies the device-permission signal that gates the IDFA. Prompt timing drives opt-in rate, which we get into over in the ATT opt-in benchmark breakdown.

4. Let the SDK read the TCF string and IDFA, then transmit. Once both signals resolve, the SDK reads the consent string from NSUserDefaults/SharedPreferences, attaches the IDFA if allowed, and flushes the queued install event.

5. Handle the EEA DMA signal separately from ATT. This is where teams cut corners. DMA consent for EEA users is its own value, collected via the CMP, effective March 6, 2024 per Adjust's DMA documentation. Do not derive it from the ATT response. Google rejects that.

Be honest about the number of gates in this chain. Your MMP has consent APIs. Your CMP has a consent API. And if you run a separate analytics SDK, that layer carries its own consent surface too — an analytics platform like Kixo is one more gate to wire alongside the MMP's consent controls, so each SDK in the app respects the same user decision rather than one honoring it while another leaks.

For the adjacent iOS obligations that ride alongside consent — required-reason API declarations and manifest entries — the iOS Privacy Manifest checklist covers what the SDK must declare.

The math nobody plans for

Say your wiring is flawless. Consent collected in order, IDFA captured, TCF string attached. How much deterministic attribution actually survives?

Less than you think, because IDFA attribution needs double opt-in. The user has to allow tracking in the publisher app and in your advertised app. Singular's math is the cleanest statement of it: "a 10% opt-in on both sides gives you just a 1% IDFA rate." You multiply the two sides. That's the whole trick, and it's brutal.

Now plug in real rates. Per Adjust's 2025 benchmark data, the industry-wide ATT opt-in average sits at about 35% for Q2 2025. Sports games run around 50%. Education apps sit near 14%.

Run the multiplication with those figures, assuming both sides look like the same vertical:

Vertical Per-side opt-in Double opt-in (× itself) Usable deterministic IDFA
Sports game 50% 0.50 × 0.50 ~25%
Industry average 35% 0.35 × 0.35 ~12%
Education app 14% 0.14 × 0.14 ~2%

The sports funnel keeps a quarter of its installs deterministically attributable. The education funnel keeps roughly 200 basis points. Two percent. Your CMP wiring can be perfect and you still land on a sub-3% deterministic population in a low-opt-in vertical.

That's not a wiring failure. It's the ceiling. So for most apps the honest plan treats deterministic IDFA attribution as the thin layer on top, and puts the real measurement weight on SKAN and probabilistic modeling underneath. If your vertical lives near the education end, design your measurement around SKAN and incrementality-based measurement from day one rather than bolting it on after the IDFA population disappoints you. Incrementality — geo holdouts, PSA tests — becomes the arbiter, and we cover the setup in incrementality testing for mobile UA.

What breaks in production

Four failure modes account for most of the incidents I've seen. Treat them as QA gates before release, not post-mortems after.

SDK init isn't actually gated. The delay API is configured but the SDK still sends on launch because something else triggered a flush. Verify the install event's timestamp lands after the CMP and ATT both resolve. My vanished-install story was exactly this.

The TCF string is absent when the SDK reads storage. The CMP wrote it late, or under a key the SDK doesn't check. Read NSUserDefaults/SharedPreferences in a debug build and confirm the string is present before the send fires.

The ATT prompt fires too early. Cold-start prompts tank opt-in and, worse, can resolve before onboarding gives the user any reason to say yes. Push it into the post-onboarding window the delay APIs are built for.

ATT response treated as DMA consent. Someone maps "allowed tracking" to "DMA consent = granted." Google rejects it. Per Adjust, ATT responses "may not be used" as DMA consent values. Keep the two signals in separate fields and confirm the EEA path pulls from the CMP.

Two of these are legal traps. Two are timing traps. Catch all four in a device farm before the release ships, because none of them throw an error. They just quietly delete attribution.

Bottom line

The load-bearing takeaway is the firing order. CMP collects consent first, the MMP SDK defers its send, ATT resolves, then the install event ships with both signals attached. Get that sequence wrong and you either lose the IDFA permanently or lose the install entirely.

Keep the two consent signals independent in your head and in your code. ATT gates the IDFA. The CMP establishes legal basis. Neither substitutes for the other, and Google and Apple both enforce that separation.

And remember that compliant doesn't mean measurable. The opt-in math decides how much attribution survives your perfectly wired stack — sometimes 25%, sometimes 2%. Wire it right, then plan for the population you'll actually have.