CLAUDE LABJP
ENTERPRISE — Claude Enterprise adds richer admin analytics, model-level entitlements, and spend alertsDEVICES — Trusted Devices arrives for Team and Enterprise, verifying devices before remote Claude Code sessionsCODE — Claude Code adds org default models, readable session names, and clickable file attachmentsMODEL — Claude Sonnet 5 is the default across all plans at $2/$10 per million tokens through August 31FABLE 5 — Claude Fable 5 returns worldwide from July 1 after export controls lift, with a new cybersecurity classifierLIMITS — Claude Code weekly usage limits are up 50% through July 13; Claude Science applications close July 15ENTERPRISE — Claude Enterprise adds richer admin analytics, model-level entitlements, and spend alertsDEVICES — Trusted Devices arrives for Team and Enterprise, verifying devices before remote Claude Code sessionsCODE — Claude Code adds org default models, readable session names, and clickable file attachmentsMODEL — Claude Sonnet 5 is the default across all plans at $2/$10 per million tokens through August 31FABLE 5 — Claude Fable 5 returns worldwide from July 1 after export controls lift, with a new cybersecurity classifierLIMITS — Claude Code weekly usage limits are up 50% through July 13; Claude Science applications close July 15
Articles/Claude Code
Claude Code/2026-07-06Advanced

I Was Starting the Ad SDK Before the Consent Dialog — Fixing ATT and AdMob Ordering with Claude Code

Starting the ad SDK at launch initializes it before ATT consent, so the IDFA is all zeros and only your measurement goes thin while fill rate stays fine. Here is how I traced the scattered init paths with Claude Code and reshaped them to start ads only after ATT resolves.


Premium Article

While staging a release, I noticed something odd in the AdMob dashboard. Fill rate and eCPM looked completely normal, yet the share of impressions that came through with SKAdNetwork attribution was clearly thinner than before. The ads were serving; only the measurement of those ads was missing.

I ruled out one suspect after another, and the last one standing was initialization order. In the wallpaper app I run as an indie developer, the ad SDK was starting at launch. At that moment the App Tracking Transparency (ATT) dialog had not been shown yet, so the SDK came up with an empty IDFA. I was assembling the measurement foundation before asking for consent.

Let me start from what actually goes missing, then walk through having Claude Code list every init path, folding them into a single "start after ATT" flow, and guarding the order so it can never drift again — all with the real code.

What Goes Missing When You Start MobileAds Before the Dialog

On iOS, an app needs ATT permission to reach the advertising identifier (IDFA). Until the user taps "Allow," the IDFA returned by ASIdentifierManager is an all-zeros value.

The ad SDK reads the available identifiers and measurement settings at init time. If the IDFA is still empty here, SKAdNetwork attribution and the init payloads handed to each mediation partner get assembled on top of a pre-consent state. Serving itself does not need the IDFA, so fill rate does not drop. Only measurement suffers — which is exactly why staring at revenue numbers won't reveal it.

Init orderAd servingIDFAMeasurement (SKAdNetwork, etc.)
Ad SDK starts before ATTWorksEmpty at initGoes thin
Ad SDK starts after ATT resolvesWorksPresent if allowedLines up

Serving is unchanged, but measurement drifts. That quiet drift was the nastiest part of this ordering bug.

The Broken Order (Before)

The offending code started the ad SDK during SwiftUI app launch. The ATT request was fired later, almost as an afterthought, on some other screen after ads had already appeared a few times.

import GoogleMobileAds
 
@main
struct WallpaperApp: App {
    init() {
        // ❌ Starts the ad SDK at launch.
        // ATT consent isn't resolved yet, so the SDK inits with an empty IDFA.
        MobileAds.shared.start()
    }
 
    var body: some Scene {
        WindowGroup { RootView() }
    }
    // ATT was requested later on another screen (= too late)
}

init() runs at the very earliest stage of launch. The ATT dialog will not even display until the app becomes active, so this order makes it structurally impossible to avoid finalizing the measurement base before consent.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
You can tell when thin SKAdNetwork measurement — despite healthy fill rate — comes from starting the ad SDK before ATT, and stop guessing at the wrong cause
You can move a broken 'start ads at launch' path to a 'start after ATT resolves' shape, with before/after Swift you can paste into your own app
You can collapse every ad-start entry point into one and guard it with CI and an assertion so the ordering can never silently regress
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Claude Code2026-07-05
Rolling Out Trusted Devices for a Small Team: Enrollment, Preflight, and Rotation
How to introduce Team/Enterprise Trusted Devices for a 2-5 person team: device enrollment, an unattended-run preflight gate, and closing the gaps that appear during device rotation and offboarding.
Claude Code2026-07-05
When to Use Claude Code's Native 1M Context — and When Not To: A Cost-Based Rule
With Sonnet 5 as the default, Claude Code now handles a native 1M-token context. A big window is convenient, but every token you park in it is billed again each turn. Should you load the whole repo, or feed slices? Here is an estimable token model and a decision rule that gives a concrete answer per situation, with working code and the traps to avoid.
Claude Code2026-07-04
A 1M Context Window Is the New Default — So I Built an Admission Policy Instead of Filling It
Sonnet 5 is now the Claude Code default and native 1M context is standard. The hard errors disappeared, but a quieter kind of degradation took their place. Here is how I made it visible with a probe, plus an admission policy and an effective-token-cost view — with working code and my own measurements.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →