Skip to main content
Skip to navigation

AdMob SDK integration

Track AdMob events on iOS, legacy Android, and Google Mobile Ads Next-Gen with RevenueCat

AIAsk AIChatGPTClaude

The RevenueCat AdMob integration wraps standard AdMob loading calls with automatic event tracking. Use it with the Google Mobile Ads SDK on iOS, the legacy SDK on Android, or the Google Mobile Ads Next-Gen SDK on Android.

📘Beta feature

This feature is currently in beta.

Before integrating, install the AdMob Adapter SDK that matches your platform and Google Mobile Ads SDK generation. The legacy adapters require purchases-ios 5.89.0+ (iOS 15+) or purchases-android 10.19.0+; see the installation guide for the Next-Gen requirements.

The loadAndTrack methods automatically track the following events, captured from AdMob's callbacks with no additional code required:

  • Ad Loaded — Ad successfully loads
  • Ad Displayed — Ad impression is recorded
  • Ad Opened — Customer clicks the ad
  • Ad Revenue — Ad generates revenue (via AdMob's paid event handler)
  • Ad Failed to Load — Ad fails to load

The adapter can't track Rewarded Ad Prompt Shown and Rewarded Ad Prompt Accepted, because only your app knows when it offers a rewarded ad. Call trackRewardedAdPromptShown and trackRewardedAdPromptAccepted yourself when you show and when the user accepts the prompt (requires purchases-ios 5.92.0+ or purchases-android 10.24.0+). See Track rewarded ad prompts.

Choose an adapter​

Choose one adapter for your platform and Google Mobile Ads SDK generation:

PlatformGoogle Mobile Ads SDKCode tab
iOSGoogle Mobile Ads SDKSwift
AndroidLegacy Google Mobile Ads SDK (play-services-ads)Kotlin (Legacy AdMob)
AndroidGoogle Mobile Ads Next-Gen SDK (ads-mobile-sdk)Kotlin (Next-Gen AdMob)

The two Android adapters are mutually exclusive. The Next-Gen adapter is Kotlin-only; Java apps can use the base AdTracker APIs directly.

Quick start​

1. Import the module​

import RevenueCatAdMob

2. Replace load calls with loadAndTrack​

Replace standard AdMob load calls with loadAndTrack methods. On iOS, every loadAndTrack method offers both an async/await and a completion-handler variant — pick whichever matches your code style:

// Before
let ad = try await InterstitialAd.load(with: adUnitID, request: Request())
ad.fullScreenContentDelegate = self
self.interstitialAd = ad

// After
self.interstitialAd = try await InterstitialAd.loadAndTrack(
withAdUnitID: adUnitID,
request: Request(),
placement: "level_complete",
fullScreenContentDelegate: self
)

Implementation examples​

import RevenueCatAdMob

let bannerView = BannerView(adSize: AdSize(size: CGSize(width: 320, height: 50), flags: 0))
bannerView.adUnitID = "ca-app-pub-3940256099942544/2435281174"

bannerView.loadAndTrack(
request: Request(),
placement: "home_banner"
)

Interstitial ads​

import RevenueCatAdMob

class MyViewController: UIViewController, FullScreenContentDelegate {
private var interstitialAd: InterstitialAd?

func loadInterstitial() async {
do {
self.interstitialAd = try await InterstitialAd.loadAndTrack(
withAdUnitID: "ca-app-pub-3940256099942544/4411468910",
request: Request(),
placement: "level_complete",
fullScreenContentDelegate: self
)
} catch {
// Handle error
}
}

func showInterstitial() {
interstitialAd?.present(from: self)
}
}

Rewarded ads​

import RevenueCatAdMob

func loadRewardedAd() async {
do {
self.rewardedAd = try await RewardedAd.loadAndTrack(
withAdUnitID: "ca-app-pub-3940256099942544/1712485313",
request: Request(),
placement: "bonus_coins",
fullScreenContentDelegate: self
)
} catch {
// Handle error
}
}

func showRewardedAd() {
rewardedAd?.present(from: self) {
// User earned reward
}
}

Rewarded interstitial ads​

import RevenueCatAdMob

func loadRewardedInterstitialAd() async {
do {
self.rewardedInterstitialAd = try await RewardedInterstitialAd.loadAndTrack(
withAdUnitID: "ca-app-pub-3940256099942544/6978759866",
request: Request(),
placement: "between_levels",
fullScreenContentDelegate: self
)
} catch {
// Handle error
}
}

func showRewardedInterstitialAd() {
rewardedInterstitialAd?.present(from: self) {
// User earned reward
}
}

App open ads​

import RevenueCatAdMob

func loadAppOpenAd() async {
do {
self.appOpenAd = try await AppOpenAd.loadAndTrack(
withAdUnitID: "ca-app-pub-3940256099942544/5575463023",
request: Request(),
placement: "app_launch",
fullScreenContentDelegate: self
)
} catch {
// Handle error
}
}

Native ads​

import RevenueCatAdMob

let adLoader = AdLoader(
adUnitID: "ca-app-pub-3940256099942544/3986624511",
rootViewController: self,
adTypes: [.native],
options: nil
)
adLoader.delegate = self

adLoader.loadAndTrack(
Request(),
placement: "feed",
nativeAdDelegate: self
)

Placement parameter​

The placement parameter identifies where ads appear in your app and is used for reporting and segmentation in Ad Charts.

Use consistent, descriptive names like "home_banner" or "level_complete_interstitial". Avoid dynamic values or timestamps.

Show-time placement override​

For full-screen ads, you can set the placement when showing the ad instead of at load time. This is useful when you preload an ad before knowing where it will be shown:

// Load once, without a specific placement
InterstitialAd.loadAndTrack(
withAdUnitID: adUnitID,
request: Request(),
fullScreenContentDelegate: self
) { [weak self] ad, error in
self?.cachedAd = ad
}

// Show with placement depending on where the user is
cachedAd?.present(from: self, placement: "level_complete")

The show-time placement applies to Ad Displayed, Ad Opened, and Ad Revenue events. Ad Loaded and Ad Failed to Load always use the load-time placement.

Optional parameters and callbacks​

Beyond placement, the loadAndTrack methods accept the load and lifecycle callbacks provided by each Google Mobile Ads SDK:

InterstitialAd.loadAndTrack(
withAdUnitID: adUnitID,
request: Request(),
placement: "level_complete", // Optional: for reporting
fullScreenContentDelegate: self, // Optional: ad lifecycle callbacks
paidEventHandler: { adValue in
// Optional: your custom paid event handling
}
) { ad, error in
// Optional: your load completion handler
}

RevenueCat wraps your lifecycle callback at load time and forwards events to your handler after tracking them. If you change the callback after loading, use the tracking-aware setter instead of assigning it directly:

// Pass the full-screen content delegate when loading the ad.
InterstitialAd.loadAndTrack(
withAdUnitID: adUnitID,
request: Request(),
placement: "level_complete",
fullScreenContentDelegate: self
) { ad, error in }

// Or replace it later with the tracking-safe setter.
ad?.setTrackingFullScreenContentDelegate(myDelegate)

// Don't assign it directly. This replaces RevenueCat's tracking wrapper.
ad?.fullScreenContentDelegate = myDelegate

Use setTrackingFullScreenContentDelegate on iOS or setTrackingFullScreenContentCallback with the legacy Android adapter. The Android Next-Gen adapter uses setTrackingAdEventCallback for every supported ad type and setTrackingBannerAdRefreshCallback for banner refresh events.

The delegate/callback parameter on loadAndTrack methods is optional and defaults to none, so you can omit it at load time and set it later with the tracking-aware setter.

Android Next-Gen features​

The Android Next-Gen adapter supports additional Google Mobile Ads Next-Gen loading workflows. These APIs are only available in the Kotlin (Next-Gen AdMob) integration.

Suspending loads​

Interstitial, rewarded, rewarded interstitial, app open, and native helpers have suspending overloads. They return Google's original result with RevenueCat tracking installed on successful ads:

lifecycleScope.launch {
when (
val result = Purchases.sharedInstance.adTracker.loadAndTrackInterstitialAd(
adRequest = AdRequest.Builder("AD_UNIT_ID").build(),
placement = "game_interstitial",
)
) {
is AdLoadResult.Success -> interstitialAd = result.ad
is AdLoadResult.Failure -> interstitialAd = null
}
}

Direct banner loading remains callback-based because Google doesn't provide a supported suspending AdView.loadAd API.

Preloading​

All six supported formats work with Google's preloaders. Track preload success or failure with startAndTrack, then install lifecycle tracking on a buffered ad with pollAndTrackAd:

val preloadConfiguration = PreloadConfiguration(
request = AdRequest.Builder("AD_UNIT_ID").build(),
bufferSize = 2,
)

InterstitialAdPreloader.startAndTrack(
preloadId = "game-interstitial-buffer",
preloadConfiguration = preloadConfiguration,
placement = "game_interstitial_preload",
preloadCallback = object : PreloadCallback {},
)

// Later, adopt a buffered ad. Polling doesn't emit another loaded event.
interstitialAd = InterstitialAdPreloader.pollAndTrackAd(
preloadId = "game-interstitial-buffer",
placement = "game_interstitial",
adEventCallback = interstitialAdEventCallback,
)

interstitialAd?.show(this)

The placements supplied to startAndTrack and pollAndTrackAd are independent. Polling doesn't emit another loaded event. Use the corresponding preloader extensions for banner, app open, native, rewarded, and rewarded interstitial ads.

Other loading workflows​

The Next-Gen adapter also supports server-to-server ad responses for every format and single or batched native-ad loading. See the Android Next-Gen advanced loading guide for complete examples.

Migrate from the legacy Android adapter

The Next-Gen adapter isn't a drop-in binary replacement for the legacy adapter. Migrate the Google Mobile Ads SDK and RevenueCat adapter together:

  1. Replace the legacy dependencies with purchases-admob-next-gen and ads-mobile-sdk.
  2. Change RevenueCat imports from com.revenuecat.purchases.admob to com.revenuecat.purchases.admob.nextgen.
  3. Update Google imports from com.google.android.gms.ads to com.google.android.libraries.ads.mobile.sdk.
  4. Update requests and callbacks to the Next-Gen types shown in this guide.
  5. Keep existing placement values when they identify the same ad slots so reporting remains consistent.

Next-Gen requests own the ad unit ID. BannerAdRequest also owns the banner size that the legacy SDK stored on AdView. Next-Gen callbacks may run on a background thread, so dispatch to the main thread before updating UI-confined state.

Review Google's Next-Gen migration guide for SDK and mediation changes.

Troubleshooting​

Testing your integration​

Before troubleshooting, verify your events are being tracked:

  1. Run your app in debug mode
  2. Navigate to the Ads page in your RevenueCat dashboard
  3. Toggle on "Sandbox data"
  4. Trigger some ads in your app
  5. Check that events appear in the sandbox events table

Events from debug builds are automatically marked as sandbox. This lets you verify your integration without affecting production data.

Note on event timing: Ad events are not synced to the dashboard in real-time. The SDK batches and sends events periodically. If you've triggered several ads and don't see events immediately, try putting your app in the background and bringing it back to the foreground to trigger a sync. This is normal behavior and doesn't indicate an integration issue.

Events not appearing in charts​

  1. Verify that "Impression-level ad revenue" is enabled in your AdMob account (instructions)
  2. Verify you've opted in via the Ads page in your RevenueCat dashboard
  3. Check that you're using loadAndTrack methods (not standard AdMob methods)
  4. Ensure placement parameter is provided
  5. Verify ads are actually loading and showing (check AdMob callbacks)
  6. Use the sandbox data view (above) to confirm events are being received

Next steps​

Was this page helpful?