ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-06-28Advanced

Adding Mediation Partners Quietly Starved My iOS Attribution — Reconciling SKAdNetwork IDs Across Four Apps

I added mediation partners but iOS revenue barely moved — the cause was missing SKAdNetwork IDs in Info.plist. Here is how I reconciled SKAdNetworkItems across four apps, using an Antigravity agent as the matcher while keeping the revenue decisions by hand.

skadnetworkadmob16mediation6ios33app-dev43antigravity399

Premium Article

The week I added three Bidding-type partners to my mediation stack, iOS requests went up but revenue barely moved. The same change lifted Android cleanly, so for a while I assumed it was just thin inventory. What didn't add up was the asymmetry: in the AdMob dashboard every network was responding — the slots were being filled — yet eCPM stayed sluggish on iOS only.

The cause wasn't inventory. It was measurement. I had never registered the new partners' SKAdNetworkID values in the SKAdNetworkItems array of Info.plist, so installs coming through those networks were invisible to iOS attribution. When measurement thins out, optimization has nothing to learn from, bids don't climb, and eCPM stalls. The ads serve, but the revenue doesn't follow — a confusingly quiet failure.

Running four iOS apps as an indie developer (wallpaper and relaxation titles at Dolice), this kind of silent decay is the opponent I fear most. A crash sets off Crashlytics; a missing SKAdNetwork registration throws no error at all — revenue just fails to grow. This is the record of how I isolated it and built a routine to keep Info.plist aligned across all four apps, using an Antigravity agent as the matcher.

Suspect Measurement, Not Inventory, When "Filled but Flat"

The first thing I checked was that match rates for each network showed up in the AdMob mediation report, yet the optimized (Bidding) networks contributed far less than expected — on iOS only. The gap versus Android was the clue. Android has no OS-level attribution registration like SKAdNetwork, so adding an adapter measures it immediately. If only iOS lags, the iOS-specific measurement path — SKAdNetwork — is the natural suspect.

The isolation order goes like this. First confirm the ad is actually serving (is there an impression?) using Ad Inspector. Then, if it serves but optimization won't converge, suspect whether installs from that network are being measured at all. SKAdNetwork only delivers a postback (the install notification) when the ad network's ID is pre-registered in the app's Info.plist. Without it, installs through that network become "nobody's win," and bid optimization gets no fuel.

SymptomCommon misdiagnosisActual cause
New partner's eCPM flat on iOS onlyThin inventory / low-CPM marketSKAdNetworkID unregistered, so measurement is missing
Responses arrive but optimization won't learnLearning period too shortPostbacks never arrive, so optimization has no fuel
Effect is uneven across four appsDifferent audiences per appInfo.plist registrations drifted between apps

Why SKAdNetworkItems Is the "Fuel" for Optimization

SKAdNetworkItems is the list of ad-network identifiers you enumerate in Info.plist. One entry looks like this:

<key>SKAdNetworkItems</key>
<array>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>cstr6suwn9.skadnetwork</string>
  </dict>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>ludvb6z3bs.skadnetwork</string>
  </dict>
  <!-- ... continue with each mediation partner's ID ... -->
</array>

Only networks whose ID is registered here can receive install postbacks through StoreKit. Adding a mediation partner effectively means "calling a new ad network," so if that network's ID isn't in Info.plist, you get a half-working state: delivery works, measurement doesn't.

The crucial point is that adding the AdMob adapter (the Pod or SPM package) and updating SKAdNetworkItems are two separate actions. Adding the adapter starts delivery. To start measurement, you must hand-add the network's published SKAdNetworkID to Info.plist. Because the two are decoupled, "adapter added but ID forgotten" drift is especially easy across multiple apps.

My four apps share nearly identical ad setups, so they should have carried the same ID set. In reality, small drifts had accumulated — one app had Liftoff's ID while another didn't. Edit Info.plist by hand four times and you'll skip one about once. That was the real shape of the "uneven decay."

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
Why adding adapters without updating SKAdNetworkItems in Info.plist quietly starves iOS attribution and depresses eCPM
A reconciliation script that diffs each network's public ID against what is registered, plus the exact agent prompt to drive it
A division of labor — humans decide which IDs, the agent does the tedious matching — enforced by a pre-build gate
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

Antigravity 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

App Dev2026-06-25
An Agent Granted 'Watch an Ad to Unlock a Wallpaper' Entirely Client-Side — Re-Verifying Reward Grants with AdMob SSV
I asked an Antigravity agent to wire up 'watch a rewarded ad to unlock a wallpaper,' and it returned an implementation that wrote the unlock flag client-side only. Here is why that is not enough, how I re-verified the reward grant with AdMob server-side verification (SSV), and how I stopped double grants too.
App Dev2026-06-25
When Xcode Cloud's Free 25 Hours Quietly Run Out at Month's End — Field Notes on Measuring and Defending Compute Time
Before Xcode Cloud's free compute allowance drains at month's end and your builds sit queued, measure where it goes. Practical field notes on pulling usage from the App Store Connect API, stopping wasteful builds early, and trimming test runs to keep CI inside budget.
App Dev2026-06-21
The Back Button Showed an Interstitial Sometimes, Not Others — Rewriting Nested ifs Into a List of Independent Guards
Interstitial display on back press was unstable because nested if statements hid the priority between conditions. Here is how I split it into reason-returning guards and generated tests from a decision table.
📚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 →