CLAUDE LABJP
OPUS — Claude Opus 4.7 is generally available, improving software engineering, long-running coding, and higher-resolution visionAPIKEY — You can now set an expiration on API keys in the Console, with email reminders before keys valid for 7+ days expireREFLECT — A monthly recap at Settings > Reflect shows your top topics, most active day, and peak hour (beta)M365 — The Microsoft 365 connector now supports write tools for email, calendar, and OneDrive/SharePoint filesCOWORK — Cowork expands to web and mobile, bringing Chat and Cowork into one shared home across devicesDESIGN — Claude Design, a new Anthropic Labs product, lets you co-create designs, prototypes, slides, and one-pagersOPUS — Claude Opus 4.7 is generally available, improving software engineering, long-running coding, and higher-resolution visionAPIKEY — You can now set an expiration on API keys in the Console, with email reminders before keys valid for 7+ days expireREFLECT — A monthly recap at Settings > Reflect shows your top topics, most active day, and peak hour (beta)M365 — The Microsoft 365 connector now supports write tools for email, calendar, and OneDrive/SharePoint filesCOWORK — Cowork expands to web and mobile, bringing Chat and Cowork into one shared home across devicesDESIGN — Claude Design, a new Anthropic Labs product, lets you co-create designs, prototypes, slides, and one-pagers
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-10
Carrying Decisions Across Compaction with PreCompact and SessionEnd Hooks
Auto-compaction does not delete your conversation. It deletes the reasons behind it. Here is a working PreCompact / SessionEnd / SessionStart hook pipeline that rescues decisions to disk and hands them to the next session, with real code and measurements.
Claude Code2026-07-09
Is the Draft That Passed the Gate the Same One You Published?
In unattended pipelines, the file your quality gate inspected and the file you actually publish can quietly diverge. Here is a digest-bound gate receipt design, with working code and measured results from 180 days of running it.
Claude Code2026-07-09
Claude Code vs Cursor: The Definitive 2026 Comparison — Choosing the Right AI Coding Tool
A comprehensive comparison of Claude Code and Cursor across pricing, features, accuracy, and workflow. Find the AI coding tool that best fits your development style with our 2026 data-driven guide.
📚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 →