AdMob SDK integration
Quick setup for AdMob ad monetization on iOS and Android with RevenueCat
The RevenueCat AdMob integration provides helper methods that wrap standard AdMob ad loading calls. Use loadAndTrack methods to automatically track all ad events across every major AdMob format — Banner, Interstitial, Rewarded, Rewarded Interstitial, App Open, and Native.
This feature is currently in beta. To enable it, visit the Ads page in your RevenueCat dashboard to opt in.
Before integrating, install the AdMob Adapter SDK and complete the required AdMob configuration. Requires purchases-ios 5.0+ (iOS 15+) or purchases-android 8.0+, plus the Google Mobile Ads SDK.
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
Quick start
1. Import the module
- Swift
- Kotlin
@_spi(Experimental) import RevenueCatAdMob
import com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI
import com.revenuecat.purchases.Purchases
import com.revenuecat.purchases.admob.* // AdMob helper extensions
These APIs are experimental and require explicit opt-in. iOS opts in through the @_spi(Experimental) attribute on the import (shown above). On Android, annotate each class or function that calls loadAndTrack with @OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class), as shown in the examples below.
2. Replace load calls with loadAndTrack
Replace standard AdMob load calls with loadAndTrack methods:
- Swift
- Kotlin
// Before
InterstitialAd.load(with: adUnitID, request: Request()) { ad, error in
ad?.fullScreenContentDelegate = self
self.interstitialAd = ad
}
// After
InterstitialAd.loadAndTrack(
withAdUnitID: adUnitID,
request: Request(),
placement: "level_complete",
fullScreenContentDelegate: self
) { ad, error in
self.interstitialAd = ad
}
// Before
RewardedAd.load(context, adUnitId, adRequest, callback)
// After
Purchases.sharedInstance.adTracker.loadAndTrackRewardedAd(
context = context,
adUnitId = adUnitId,
adRequest = adRequest,
placement = "bonus_coins",
loadCallback = callback
)
Implementation examples
Banner ads
- Swift
- Kotlin
@_spi(Experimental) 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"
)
@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
@Composable
fun BannerAdView() {
AndroidView(
factory = { context ->
AdView(context).apply {
adUnitId = "ca-app-pub-3940256099942544/9214589741"
setAdSize(AdSize.BANNER)
// Load with tracking
Purchases.sharedInstance.adTracker.loadAndTrackBannerAd(
adView = this,
adRequest = AdRequest.Builder().build(),
placement = "home_banner"
)
}
}
)
}
Interstitial ads
- Swift
- Kotlin
@_spi(Experimental) import RevenueCatAdMob
class MyViewController: UIViewController, FullScreenContentDelegate {
private var interstitialAd: InterstitialAd?
func loadInterstitial() {
InterstitialAd.loadAndTrack(
withAdUnitID: "ca-app-pub-3940256099942544/4411468910",
request: Request(),
placement: "level_complete",
fullScreenContentDelegate: self
) { [weak self] ad, error in
if let error { return }
self?.interstitialAd = ad
}
}
func showInterstitial() {
interstitialAd?.present(from: self)
}
}
@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
class MyActivity : AppCompatActivity() {
private var interstitialAd: InterstitialAd? = null
fun loadInterstitial() {
Purchases.sharedInstance.adTracker.loadAndTrackInterstitialAd(
context = this,
adUnitId = "ca-app-pub-3940256099942544/1033173712",
adRequest = AdRequest.Builder().build(),
placement = "level_complete",
loadCallback = object : InterstitialAdLoadCallback() {
override fun onAdLoaded(ad: InterstitialAd) {
interstitialAd = ad
}
override fun onAdFailedToLoad(error: LoadAdError) {
interstitialAd = null
}
}
)
}
fun showInterstitial() {
interstitialAd?.show(this)
}
}
Rewarded ads
- Swift
- Kotlin
@_spi(Experimental) import RevenueCatAdMob
func loadRewardedAd() {
RewardedAd.loadAndTrack(
withAdUnitID: "ca-app-pub-3940256099942544/1712485313",
request: Request(),
placement: "bonus_coins",
fullScreenContentDelegate: self
) { [weak self] ad, error in
if let error { return }
self?.rewardedAd = ad
}
}
func showRewardedAd() {
rewardedAd?.present(from: self) {
// User earned reward
}
}
@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
fun loadRewardedAd(context: Context) {
Purchases.sharedInstance.adTracker.loadAndTrackRewardedAd(
context = context,
adUnitId = "ca-app-pub-3940256099942544/5224354917",
adRequest = AdRequest.Builder().build(),
placement = "bonus_coins",
loadCallback = object : RewardedAdLoadCallback() {
override fun onAdLoaded(ad: RewardedAd) {
// Ad loaded successfully
ad.show(activity) { reward ->
// User earned reward
grantCoins(reward.amount)
}
}
override fun onAdFailedToLoad(error: LoadAdError) {
// Handle error
}
}
)
}
Rewarded interstitial ads
- Swift
- Kotlin
@_spi(Experimental) import RevenueCatAdMob
func loadRewardedInterstitialAd() {
RewardedInterstitialAd.loadAndTrack(
withAdUnitID: "ca-app-pub-3940256099942544/6978759866",
request: Request(),
placement: "between_levels",
fullScreenContentDelegate: self
) { [weak self] ad, error in
if let error { return }
self?.rewardedInterstitialAd = ad
}
}
func showRewardedInterstitialAd() {
rewardedInterstitialAd?.present(from: self) {
// User earned reward
}
}
@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
fun loadRewardedInterstitialAd(context: Context) {
Purchases.sharedInstance.adTracker.loadAndTrackRewardedInterstitialAd(
context = context,
adUnitId = "ca-app-pub-3940256099942544/5354046379",
adRequest = AdRequest.Builder().build(),
placement = "between_levels",
loadCallback = object : RewardedInterstitialAdLoadCallback() {
override fun onAdLoaded(ad: RewardedInterstitialAd) {
ad.show(activity) { reward ->
// User earned reward
}
}
override fun onAdFailedToLoad(error: LoadAdError) {
// Handle error
}
}
)
}
App open ads
- Swift
- Kotlin
@_spi(Experimental) import RevenueCatAdMob
func loadAppOpenAd() {
AppOpenAd.loadAndTrack(
withAdUnitID: "ca-app-pub-3940256099942544/5575463023",
request: Request(),
placement: "app_launch",
fullScreenContentDelegate: self
) { [weak self] ad, error in
if let error { return }
self?.appOpenAd = ad
}
}
@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
fun loadAppOpenAd(context: Context) {
Purchases.sharedInstance.adTracker.loadAndTrackAppOpenAd(
context = context,
adUnitId = "ca-app-pub-3940256099942544/9257395921",
adRequest = AdRequest.Builder().build(),
placement = "app_launch",
loadCallback = object : AppOpenAd.AppOpenAdLoadCallback() {
override fun onAdLoaded(ad: AppOpenAd) {
// Ad loaded successfully
}
override fun onAdFailedToLoad(error: LoadAdError) {
// Handle error
}
}
)
}
Native ads
- Swift
- Kotlin
@_spi(Experimental) 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
)
@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
val adLoader = AdLoader.Builder(context, "ca-app-pub-3940256099942544/2247696110")
// Use forNativeAdWithTracking in place of the standard forNativeAd
.forNativeAdWithTracking(
adUnitId = "ca-app-pub-3940256099942544/2247696110",
placement = "feed",
) { nativeAd ->
// Use the loaded native ad
}
.build()
// Load as usual with the standard AdLoader.loadAd() call
adLoader.loadAd(AdRequest.Builder().build())
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:
- Swift
- Kotlin
// 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")
// Load once, without a specific placement
Purchases.sharedInstance.adTracker.loadAndTrackInterstitialAd(
context = context,
adUnitId = adUnitId,
adRequest = AdRequest.Builder().build(),
loadCallback = object : InterstitialAdLoadCallback() {
override fun onAdLoaded(ad: InterstitialAd) {
cachedAd = ad
}
}
)
// Show with placement depending on where the user is
cachedAd?.show(activity, "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, all loadAndTrack methods accept optional parameters — a load completion/callback, a full-screen content delegate/callback, and a paid-event handler/listener:
- Swift
- Kotlin
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
}
Purchases.sharedInstance.adTracker.loadAndTrackInterstitialAd(
context = context,
adUnitId = adUnitId,
adRequest = adRequest,
placement = "level_complete", // Optional: for reporting
loadCallback = callback, // Optional: your load callback
fullScreenContentCallback = contentCallback, // Optional: for ad lifecycle
onPaidEventListener = paidListener // Optional: your paid event listener
)
RevenueCat wraps your full-screen content delegate (iOS) or callback (Android) at load time to add tracking before forwarding events to your handlers. If you need to set or change it after loading, use the tracking-aware setter instead of assigning it directly:
- Swift
- Kotlin
// ✅ Set delegate at load time
InterstitialAd.loadAndTrack(
withAdUnitID: adUnitID,
request: Request(),
placement: "level_complete",
fullScreenContentDelegate: self
) { ad, error in }
// ✅ Or set/change delegate after load — safe
ad?.setTrackingFullScreenContentDelegate(myDelegate)
// ❌ Don't do this — overwrites tracking
ad?.fullScreenContentDelegate = myDelegate
import com.revenuecat.purchases.admob.setTrackingFullScreenContentCallback
// ✅ Set callback at load time
Purchases.sharedInstance.adTracker.loadAndTrackInterstitialAd(
context = context,
adUnitId = adUnitId,
adRequest = adRequest,
placement = "level_complete",
fullScreenContentCallback = myCallback
)
// ✅ Or set/change callback after load — safe
ad.setTrackingFullScreenContentCallback(myCallback)
// ❌ Don't do this — overwrites tracking
ad.fullScreenContentCallback = myCallback
The tracking-aware setter — setTrackingFullScreenContentDelegate on iOS and setTrackingFullScreenContentCallback on Android — is available on all full-screen ad types: InterstitialAd, AppOpenAd, RewardedAd, and RewardedInterstitialAd.
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.
Troubleshooting
Testing your integration
Before troubleshooting, verify your events are being tracked:
- Run your app in debug mode
- Navigate to the Ads page in your RevenueCat dashboard
- Toggle on "Sandbox data"
- Trigger some ads in your app
- 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
- Verify that "Impression-level ad revenue" is enabled in your AdMob account (instructions)
- Verify you've opted in via the Ads page in your RevenueCat dashboard
- Check that you're using
loadAndTrackmethods (not standard AdMob methods) - Ensure
placementparameter is provided - Verify ads are actually loading and showing (check AdMob callbacks)
- Use the sandbox data view (above) to confirm events are being received
Compilation errors
Make sure you've added the experimental opt-in — the @_spi(Experimental) import on iOS, or the @OptIn annotation on Android:
- Swift
- Kotlin
@_spi(Experimental) import RevenueCatAdMob
@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
Next steps
- Explore the example projects: iOS and Android
- View your ad data in Ad Charts, and see the Ad Monetization overview for where it appears across the dashboard
- Connect your Google AdMob account to sync ad unit names for more readable charts — no code changes required
- Learn about Manual Integration for other mediation platforms