11.9 — Ad Monetization & SKAdNetwork
Opening scenario
You ship a free utility app — flashlight, calculator, currency converter — pick your noun. After two weeks you have 50,000 users and zero revenue. You drop in AdMob, take a week to integrate, and watch the dashboard: $0.42 the first day. Twenty users hit the ATT prompt and 18 said “Ask App Not to Track.” Your eCPM is $0.30 instead of the $5 you saw in last year’s blog post. You don’t have an ad strategy — you have an ad widget. The actual ad business is a stack: networks, mediation, attribution, ATT consent, SKAdNetwork postbacks, fraud detection, eCPM optimization. This chapter is the field guide.
Context taxonomy
| Layer | Examples | Purpose |
|---|---|---|
| Ad networks | AdMob (Google), Meta Audience Network, Unity Ads, ironSource, Vungle | Sources of demand |
| Mediation | AdMob Mediation, Max (AppLovin), LevelPlay (ironSource), TradPlus | Pick the highest-bidding network per impression |
| MMP (Mobile Measurement Partner) | Adjust, Appsflyer, Branch, Kochava, Singular | Attribute installs to ad campaigns |
| ATT (App Tracking Transparency) | ATTrackingManager.requestTrackingAuthorization() | User permission to use IDFA cross-app |
| SKAdNetwork | Apple framework, version 4 in 2026 | Privacy-preserving install attribution without IDFA |
| AdServices | Apple framework | Apple Search Ads attribution |
| Ad formats | Banner, Interstitial, Rewarded video, Native, App Open | Different RPM tiers |
| Privacy frameworks | App Privacy Report, Privacy Manifest required 2024+ | Disclosure of tracking |
Concept → Why → How → Code
Concept. Ad monetization is a multi-layer stack: networks bring demand, mediation routes impressions to highest bidders, attribution closes the loop from campaign spend to installs and post-install events, and Apple’s privacy frameworks (ATT, SKAdNetwork, Privacy Manifest) gate everything.
Why. Ad ARPU is small per user ($0.50–$5/yr) but scales to massive numbers (millions of MAU). Apps with hundreds of millions of users — TikTok, Snapchat, ad-supported games — earn most of their revenue from ads. For most indie apps, ads at small scale are pocket change; at 1M+ MAU, ads become a viable business.
Ad network landscape (2026 eCPM benchmarks)
| Network | Best for | iOS eCPM (US, post-ATT) | Notes |
|---|---|---|---|
| Google AdMob | Default integration, broad demand | $3–8 banner, $15–40 rewarded | Lowest ops burden |
| Meta Audience Network | Casual games, social | $2–7 banner, $10–30 rewarded | Recovered post-ATT through SKAd |
| Unity Ads | Games (Unity-shipped) | $5–15 interstitial, $20–50 rewarded | Strong rewarded video |
| ironSource (Unity) | Games mediation + own demand | similar | Often part of LevelPlay mediation |
| AppLovin / Max | Mediation default 2026 | $4–10 interstitial avg | Most respected mediation; Max audited |
| Vungle (Liftoff) | Mid-tier video, casino | $8–20 video | Niche but strong |
| TikTok Ads (Pangle) | High demand 2026 | $3–8 banner, $15–40 rewarded | Fast-growing demand |
eCPM: effective revenue per 1,000 impressions. Rewarded video > Interstitial > Native > Banner > App Open in revenue, by roughly that order.
Ad format strategy
Banner ← always-on, low intrusion, $0.30–$3 eCPM
Interstitial ← full-screen between game levels, $3–10 eCPM (don't show > 1/min)
Rewarded video ← user opts in for in-game reward, $10–50 eCPM, BEST format
Native ← matches app UI, $1–5 eCPM, hard to integrate cleanly
App Open ← shown on cold launch, $1–4 eCPM, polarizing UX
The dominant 2026 pattern for free apps with revenue: rewarded video + interstitial mediation. Banner-only is a 2018 strategy and earns coffee money.
App Tracking Transparency (ATT) — the consent gate
// AppDelegate.swift or main App init
import AppTrackingTransparency
import AdSupport
func requestTrackingPermission() async {
guard ATTrackingManager.trackingAuthorizationStatus == .notDetermined else { return }
// Wait briefly for app to settle (Apple recommends not asking on first launch)
try? await Task.sleep(nanoseconds: 1_500_000_000)
let status = await ATTrackingManager.requestTrackingAuthorization()
switch status {
case .authorized:
let idfa = ASIdentifierManager.shared().advertisingIdentifier
// IDFA available for cross-app tracking
case .denied, .restricted, .notDetermined:
// IDFA returns 00000000-0000-0000-0000-000000000000
break
@unknown default: break
}
}
Info.plist requirement:
<key>NSUserTrackingUsageDescription</key>
<string>We use this to show you more relevant ads and improve our app.</string>
ATT opt-in rates in 2026:
- Top-tier apps with great pre-prompt explanation: 30–45%
- Average: 20–30%
- Poor pre-prompts or no explanation: 10–15%
A “pre-prompt” is your own custom UI shown before triggering the system prompt — explaining why and what the user gains by opting in. Big lift on opt-in rate.
SKAdNetwork — privacy-preserving attribution
When ATT is denied (most users), advertisers can’t track installs via IDFA. SKAdNetwork is Apple’s replacement: cryptographically signed postbacks that tell the ad network “an install happened” without revealing the user’s identity.
Flow:
1. User taps ad in App A (publisher)
2. App A calls SKAdNetwork.startImpression(...)
3. User taps ad → App B (advertised app) installs
4. App B calls SKAdNetwork.updatePostbackConversionValue(...) per user activity
5. 24h+ random delay → Apple sends signed postback to App B's ad network
6. Postback includes: ad campaign ID, ad network ID, conversion value (0-63),
but NOT user ID
// App B (the advertised app) — on first launch
import StoreKit
SKAdNetwork.registerAppForAdNetworkAttribution() // SKAdNetwork 1.0 legacy
// Or modern:
do {
try await SKAdNetwork.updatePostbackConversionValue(0)
} catch { /* handle */ }
// As user completes onboarding / purchase, increase conversion value
do {
try await SKAdNetwork.updatePostbackConversionValue(15,
coarseValue: .high,
lockWindow: false)
} catch { /* handle */ }
Conversion value is a 6-bit number (0–63) you encode meaning into. Typical encoding:
- 0: install only
- 1–10: completed onboarding
- 11–30: in-app purchase, dollar bins
- 31–63: high-value events (subscription, repeat purchase)
Your ad network defines the encoding; you implement it; their dashboard decodes it.
AdServices — Apple Search Ads attribution
import AdServices
if let token = try? AAAttribution.attributionToken() {
// Send to your server
let url = URL(string: "https://api-adservices.apple.com/api/v1/")!
var req = URLRequest(url: url)
req.httpMethod = "POST"
req.httpBody = token.data(using: .utf8)
req.setValue("text/plain", forHTTPHeaderField: "Content-Type")
let (data, _) = try await URLSession.shared.data(for: req)
// data contains: campaignId, adgroupId, keywordId, etc.
}
This is the only deterministic install attribution still allowed for Apple Search Ads. Other ad networks use SKAdNetwork.
Privacy Manifest (required since 2024)
Apple requires PrivacyInfo.xcprivacy declaring:
- Data types collected
- Tracking domains
- Required Reason APIs used (e.g.,
UserDefaults,SystemBootTime)
<dict>
<key>NSPrivacyTracking</key>
<true/>
<key>NSPrivacyTrackingDomains</key>
<array>
<string>ads.example-network.com</string>
<string>track.appsflyer.com</string>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeDeviceID</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<true/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<true/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAdvertising</string>
</array>
</dict>
</array>
</dict>
Apple rejects builds without correct Privacy Manifests as of 2024.
Mediation example with AdMob
import GoogleMobileAds
// Initialize
@main
struct MyApp: App {
init() {
MobileAds.shared.start()
}
}
// Rewarded video
final class RewardedAdLoader {
private var rewardedAd: RewardedAd?
func load() async throws {
rewardedAd = try await RewardedAd.load(
with: "ca-app-pub-XXX/YYY",
request: Request()
)
}
@MainActor
func show(from vc: UIViewController) async throws -> Bool {
guard let ad = rewardedAd else { return false }
return await withCheckedContinuation { cont in
ad.present(from: vc) {
let reward = ad.adReward
cont.resume(returning: true)
Task { try? await self.load() } // Pre-load next
}
}
}
}
AdMob’s mediation feature lets you bid out impressions to AppLovin, Meta, Unity, etc. — auctioning each impression in real-time. eCPM uplift of 10–30% typical.
When ads destroy UX (anti-pattern checklist)
❌ Interstitial on every screen transition (Apple Review flags this) ❌ App Open ad shown on every cold launch (frustrating; bad reviews) ❌ Banner that overlaps content (Apple Review rejects) ❌ Misleading “X” button that opens ad instead of closes ❌ Auto-play video with sound on ❌ More than ~3% of session time spent on ads ❌ Ad in onboarding flow (kills retention) ❌ Cannot close ad without watching full 30 seconds
The line: ads should feel like the occasional cost of using a free product, not the product itself. Apps that cross the line get 1-star reviews and tanked retention.
In the wild
- TikTok earns ~$15B/yr from ads. Almost zero in-app purchases relative to ad revenue.
- Duolingo’s free tier shows interstitials every ~3 lessons; rewarded video for hearts. ~30% of revenue from ads, 70% from Super.
- Royal Match (King) — runs almost 100% on interstitials and rewarded video; $2B+ annual revenue.
- Snapchat — ads, AR filter purchases, Snap+ subscription. Ads dominant.
- Reddit official app — runs banner + native ads. Eyeballed eCPM higher than expected because of strong audience targeting.
Common misconceptions
- “Ad ARPU is high.” Median ad ARPU is $1–3/user/year. You need huge MAU before ads pay rent.
- “ATT killed mobile ads.” It killed deterministic cross-app tracking. SKAdNetwork attribution still works at lower fidelity. Ad spend shifted, didn’t vanish.
- “Banner ads are the default.” They’re the lowest-revenue format. Rewarded video is 20–100× more lucrative per impression. Default to rewarded video where it fits.
- “You can skip ATT prompt if you’re not tracking.” You don’t need to ask permission if you don’t use IDFA. But not asking means SKAdNetwork-only attribution — works but more limited.
- “Mediation is too complex for indies.” AdMob Mediation is a couple of config screens. Doubles eCPM. Skip it at your peril.
Seasoned engineer’s take
TIP. Always pre-prompt before showing the ATT system dialog. Explain what the user gets by allowing tracking (more relevant ads, fewer irrelevant ones). Opt-in rates can double.
WARNING. App Store Review polices ad density aggressively. More than ~3 interstitials per minute, App Open + immediate interstitial, or anything that makes the app feel like an ad delivery vehicle gets rejected.
The right way to think about ads: ads are a layer on top of a great free experience, not a substitute for one. The most lucrative ad-funded apps (TikTok, Duolingo, Royal Match) are also the most loved. Bad UX + ads = bad reviews and zero retention; great UX + ads = sustainable business at scale.
Interview corner
Junior — “What is ATT?” App Tracking Transparency — Apple’s permission framework requiring user consent before an app can use IDFA for cross-app tracking. Introduced iOS 14.5.
Mid — “How do ads still get attributed after ATT?” SKAdNetwork — Apple’s cryptographically-signed postback system that tells ad networks when installs happen without revealing user identity. Conversion value (0–63) encodes app-defined success events.
Senior — “Design ad monetization for a free utility app at 5M MAU.” Default rewarded video for premium features; light interstitial after natural pause points (post-result screens). AdMob with Mediation to AppLovin Max, Meta, Unity for bidding. ATT pre-prompt explaining relevance benefit; opt-in target 30%. SKAdNetwork conversion values encoded for in-app conversions. Privacy Manifest declared. Annual eCPM optimization review: rotate mediation waterfall based on actual fill + eCPM data per network.
Red flag — “We ask for ATT permission immediately on first launch with no explanation.” Opt-in rate will be <15%. Add a pre-prompt screen explaining the benefit; conversion can double.
Lab preview
The labs focus on subscriptions, which are the dominant 2026 monetization. Ad integration is well-supported by SDK setup wizards from AdMob/Max — not a unique learning surface, but the strategic patterns from this chapter apply directly.