The consent banner appears on first launch, the user taps accept or decline, and the analytics SDK has already sent three events. That sequence is the most common privacy defect in mobile apps, it is almost never deliberate, and it happens because SDK initialisation runs at application startup while consent is a screen that comes later.
Short answer
An analytics SDK initialised in your application entry point starts collecting before any user has agreed to anything. Making consent meaningful means deferring initialisation until after the decision, not calling a disable method afterwards. Both stores require accurate disclosure of what an app collects, under Apple's user privacy and data use rules and Google's user data policy, and the disclosure has to describe what the build does rather than what the design intended.
Why the ordering goes wrong
The structural reason is that SDK setup and user interface are different lifecycle stages, and consent lives in the second one.
Most SDK documentation tells you to initialise as early as possible, usually in the application delegate or the application class, because early initialisation captures more sessions and produces cleaner data for the vendor. That advice is written from the vendor's perspective and it directly conflicts with a consent-first design.
The second reason is that initialisation often does more than register a library. Many SDKs send an install or session event at setup, read an advertising identifier, start a background uploader, or register lifecycle observers that fire on the next foreground transition. All of that happens before your consent screen has drawn a single pixel.
The third reason is that disabling after the fact feels equivalent and is not. Calling a method that turns collection off does nothing about the events already queued or sent, and in several SDKs it stops future collection while leaving the identifier already assigned to that device in the vendor's system.
And the fourth is that nobody tests for it, because the app behaves correctly from the user's point of view. The banner appears, the choice is respected going forward, and the only way to see the problem is to watch the network.
What a defensible flow looks like
Do not initialise until you have an answer. Keep the SDK objects uncreated and their setup calls unmade until the user has made a choice, storing that choice somewhere durable so subsequent launches skip the prompt without skipping the check.
Treat no answer as no consent. A user who dismissed the screen, force-quit during it, or has not reached it yet has not agreed, and the default has to be non-collection rather than collection pending refusal.
Handle each purpose separately if you ask separately. If your prompt distinguishes analytics from advertising from crash reporting, then the code has to distinguish them too, and a single boolean gating all three makes the prompt inaccurate.
Make withdrawal work. A user who changes the setting later should stop being collected from, and where the SDK supports deletion, that request should be forwarded rather than only honoured locally.
Keep the consent record. What was agreed, when, and against which version of your prompt. This is the evidence that the choice was made, and reconstructing it later from application logs is not the same thing.
The SDKs that make this hard
Some libraries are structurally difficult to defer, and knowing which before you integrate saves a rewrite.
Libraries with static initialisation that runs on class load rather than on an explicit setup call. These begin working the moment the class is touched, which can be earlier than you think.
Libraries that require initialisation before any other call, including the call that would configure their consent state. This ordering requirement is documented in some SDKs and is precisely backwards for a consent-first app.
Libraries bundled inside other libraries. An SDK that includes an analytics component you did not choose is common in advertising, attribution and support tooling, and its behaviour is invisible from your dependency declaration.
Libraries whose consent mode still collects. Several vendors offer a restricted mode that continues sending a subset of data, which may be defensible and is not the same as sending nothing, and describing it as consent-gated in your documentation would be inaccurate.
The practical response is to evaluate this at integration time by watching traffic, rather than by reading the vendor's privacy page.
Checking it properly
The only reliable test is observation, because every other method describes what someone intended.
Install a fresh build on a device with a proxy in place, launch it, and do nothing. Record every request before the first user interaction. That list is what your app collects without consent, regardless of what your code appears to say.
Then decline consent and repeat. Anything still being sent is either a legitimate operational call or a defect, and it needs to be classified explicitly rather than assumed to be the former.
Then accept, and confirm that what is now sent matches your store privacy disclosure field by field. This is the comparison a reviewer or regulator makes, and doing it yourself first is considerably cheaper.
Repeat after every SDK addition or major version bump. Vendors change defaults between releases, and a library that respected your ordering last year may initialise differently now.
The client-side inventory is visible in the built artifact, so an automated pass such as PTKD.com can tell you which analytics, advertising and attribution SDKs are actually present and how they are configured. That is a useful starting point on an inherited codebase, and the runtime ordering question still requires watching a launch.
Where the requirements come from
It helps to separate the sources, because teams tend to treat them as one rule and they impose different obligations.
Store policy requires accurate disclosure, and enforces it at review. Apple's privacy labels and Google's data safety section are declarations that must match behaviour, and both platforms treat a mismatch as a policy violation independent of any law.
Privacy law requires a lawful basis for processing and, in several regimes, consent before non-essential collection. That obligation exists whether or not a store checks it, and it is what makes the ordering question legally relevant rather than merely a policy detail.
Platform tracking rules add a third layer, governing use of tracking identifiers and cross-app measurement, with their own prompts and permissions.
The MASVS privacy category is the engineering-side framing of the same ground, and the App Review Guidelines and Google Play policies are the enforcement documents. Building to the strictest of the three is simpler than tracking which applies where.
Retrofitting consent into an existing app
Most teams meet this on a codebase where the SDKs were added first, which is a different job from designing it correctly.
Start by finding every initialisation site rather than every tracking call. The tracking calls are usually well behaved and behind a wrapper; the initialisation is scattered across the application entry point, a few view controllers and occasionally a library's own automatic setup.
Then introduce a single gate that everything passes through. One place that knows the consent state and owns the decision to initialise, rather than a boolean checked in twenty places. The value of this is not elegance, it is that a future SDK addition has one obvious place to be wired in.
Then move initialisation behind the gate one library at a time, watching traffic between each move. Doing them all at once makes it impossible to tell which change fixed what, and some libraries misbehave when deferred in ways you want to attribute correctly.
Then deal with the libraries that cannot be deferred. Options are replacing them, configuring whatever restricted mode they offer and disclosing it accurately, or accepting the collection as necessary and being able to justify why. Pretending they are gated when they are not is the only option that is clearly wrong.
Finally, update the store privacy disclosure to match the new behaviour, because a retrofit that improves the app and leaves the declaration describing the old behaviour has fixed half the problem.
The wrapper pattern that keeps this working
The reason consent implementations decay is that the next SDK gets added by someone who was not part of the original work.
A thin internal wrapper around analytics, with initialisation controlled centrally and no direct SDK calls anywhere else, makes the correct path the easy path. New tracking goes through the wrapper because that is the only interface available, and new SDKs get registered with the gate because the gate is where initialisation lives.
Add a lint rule or a review checklist item forbidding direct SDK imports outside the wrapper if the team is large enough for that to matter. It is a small amount of process that prevents the specific regression that undoes this work.
The disclosure side, which is where enforcement happens
Engineering effort tends to go into the consent flow, and the thing reviewers and regulators actually compare is your declaration against your traffic.
Write the declaration from observed behaviour. Fill in the privacy label or data safety section after running the proxy test, not before, and use the list of endpoints you recorded as the source. Declarations written from memory or from a product spec are wrong more often than they are right.
Include third-party collection. Both platforms hold you responsible for what SDKs in your build collect, not only for what your own code sends. This is the most common gap on inherited codebases, where nobody knows the full dependency list.
Update it when SDKs change. A declaration is a point-in-time statement, and adding an attribution library six months later changes what is true without changing what is declared.
Keep the evidence. The proxy capture, the date, the build version. If a store or a customer's security team questions a declaration, showing how it was derived is a much stronger position than restating it.
What accurate looks like when the answer is awkward
Sometimes the honest declaration is less flattering than teams would like, and the temptation is to describe intent rather than behaviour.
An SDK that collects a device identifier for fraud prevention collects a device identifier. Describing that as no data collection because the purpose is defensive is inaccurate, and the purpose belongs in the explanation rather than in the answer to whether collection occurs.
A crash reporter attaching a user reference is collecting an identifier. A support SDK reading contact information is collecting contact information. In each case the accurate declaration plus a clear purpose is a better position than a clean declaration that does not survive inspection.
One habit that keeps it honest
Add the proxy launch test to your release checklist next to whatever else you check before submitting.
It takes five minutes, needs no tooling beyond a proxy you already have, and answers the only question that matters here: what does this specific build send before the user has agreed to anything. Every other artifact in this process, the code, the design document, the declaration, describes intent. This one describes behaviour, and behaviour is what gets reviewed.
What to take away
An SDK initialised at application startup collects before consent exists, and that is the defect in most apps that have a consent screen at all.
Defer initialisation rather than disabling after the fact, because disabling does nothing about events already sent or identifiers already assigned.
Treat the absence of an answer as refusal, gate each purpose you asked about separately, and make withdrawal actually stop collection rather than only hiding the setting.
Route everything through one wrapper so the next SDK added by someone who was not part of this work lands in the right place by default.
Evaluate SDKs for deferability at integration time by watching traffic, since some libraries initialise on class load, require setup before any configuration call, or bundle analytics components you did not choose.
And verify by launching a fresh build behind a proxy and recording everything sent before the first tap, because that list is the honest description of what your app collects.



