Intro
With this Plugin you can add Google Admob Ads inside your MAUI Android and iOS apps with a single line!!! This plugin supports: Banners (including collapsible banners), Interstitials, Rewarded, Rewarded Interstitials, App Open Ads, Native Ads, Google Ad Manager, Consent (UMP) and App Tracking Transparency.
Version 3.0 runs Android on Google's Mobile Ads Next-Gen SDK, with the same public API as 2.x, and requires Android 7.0 (API 24) or later. Version 2.x, on the legacy SDK, is still maintained for apps that cannot move yet. See the Version 3 (Next-Gen) section for what changed and how to update.
Version 2.5.0 and later target .NET 10. If you are still on .NET 9, use version 2.4.0.
Commercial support
If you need faster support with this plugin, or custom changes or you would like to hire me to help with the development of your project, contact me at [email protected] to discuss it further.
Important
From version 1.1.0, I've added support for the consent required by Google for all the final users living in Europe. This support for UMP is only available in the licensed version of the plugin. Without the license, the plugin will continue to work as usual with all the other features normally available. This will allow me to continue supporting the development of the plugin. You can buy the license here. If you are a charity or your app is used to support a good cause, contact me and I'll verify and send you a free license.
A 1 Major Version licence is valid for the major version it was bought for: a key bought for 2.x works on 2.x but not on 3.x. Perpetual licences are valid for all versions.
If you are not ready to buy a license, if possible, please, support my work with a few coffees or even better with a Membership! You can do it here: Buy Me A Coffee
Your help allows me to continue to spend time on this project and continue to maintain and update it with new features.
Cryptography
This plugin uses a cryptographic algorithm to handle the license.
Current Status
Latest version: 3.0.0
| Android | iOS | Windows | Mac | |
|---|---|---|---|---|
| Banner | ||||
| Native Ads | ||||
| Interstitial | ||||
| Rewarded | ||||
| Rewarded Interstitial | ||||
| Open Ads | ||||
| UMP Consent |
Methods
General
| Init |
| SetAppMuted |
| SetAppVolume |
| OpenAdInspector |
Ads
| Banner | Interstitial | Rewarded | Rewarded Interstitial | |
|---|---|---|---|---|
| LoadAd | LoadInterstitial | LoadRewarded | LoadRewardInterstitial | |
| ShowInterstitial | ShowRewarded | ShowRewardInterstitial | ||
| IsInterstitialLoaded | IsRewardedLoaded | IsRewardInterstitialLoaded | ||
| GetNumberOfInterstitialsLoaded | GetNumberOfRewardedLoaded | GetNumberOfRewardedInterstitialsLoaded | ||
| EmptyInterstitialAdsList | EmptyRewardedAdsList | EmptyRewardInterstitialAdsList |
| Native Ads | App Open Ads |
|---|---|
| LoadNativeAd | - |
| LoadNativeAds |
| Consent |
|---|
| InitialiseConsent |
| InitialiseAndShowConsentForm |
| ShowPrivacyOptionsForm |
| Reset |
Events
General
| OnMobileAdsInitialized |
| OnAdPaid (impression-level ad revenue, every ad format) |
| OnAdInspectorClosed |
Ads
| Banner | Interstitial | Rewarded | Rewarded Interstitial | App Open Ads |
|---|---|---|---|---|
| AdsLoaded | OnInterstitialLoaded | OnRewardedLoaded | OnRewardedLoaded | OnAppOpenAdLoaded |
| AdsFailedToLoad | OnInterstitialFailedToLoad | OnRewardedFailedToLoad | OnRewardedFailedToLoad | OnAppOpenFailedToLoad |
| AdsImpression | OnInterstitialImpression | OnRewardedImpression | OnRewardedImpression | OnAppOpenImpression |
| AdsClicked | OnInterstitialOpened | OnRewardedOpened | OnRewardedOpened | OnAppOpenOpened |
| AdsOpened | OnInterstitialFailedToShow | OnRewardedFailedToShow | OnRewardedFailedToShow | OnAppOpenFailedToShow |
| AdsClosed | OnInterstitialClosed | OnRewardedClosed | OnRewardedClosed | OnAppOpenClosed |
| AdsSwiped | OnInterstitialClicked | OnRewardedClicked | OnRewardedClicked | OnAppOpenClicked |
| OnUserEarnedReward | OnUserEarnedReward |
| Consent |
|---|
| OnConsentInfoUpdateSuccess |
| OnConsentInfoUpdateFailure |
| OnConsentFormDismissed |
| OnConsentFormLoadSuccess |
| OnConsentFormLoadFailure |
Version 3 (Next-Gen)
From version 3.0, the plugin runs Android on Google's Google Mobile Ads Next-Gen SDK, which Google has made the preferred SDK for Android. iOS keeps using the Google Mobile Ads SDK 12.x, because Google has not released a Next-Gen SDK for iOS.
The public API is the same as in 2.x. The namespace, the controls, the events and every method signature are unchanged, so for most apps updating is just a package update.
Which version should I use?
| 3.x | 2.x | |
|---|---|---|
| Android SDK | Google Mobile Ads Next-Gen SDK | Legacy Google Mobile Ads SDK |
| iOS SDK | Google Mobile Ads SDK 12.x | Google Mobile Ads SDK 12.x |
| Android minimum version | Android 7.0 (API 24) | Android 5.0 (API 21) |
| Mediation on Android | AdMob mediation only | Any mediation platform |
| Status | Current version | Still maintained |
Use 3.x unless your app needs an Android minimum version below 24, or mediates through a platform other than AdMob. In those cases stay on 2.x until you can move.
Updating from 2.x
1. Update Plugin.MauiMTAdmob to 3.x.
2. Raise your Android minimum version to 24 in your app's csproj:
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
3. Leave your code as it is. UseMauiMTAdmob(), CrossMauiMTAdmob.Current, MTAdView, MTNativeAdView, the events and the consent calls are unchanged. Keep the AdMob App ID in your AndroidManifest.xml: the consent SDK still reads it from there.
4. If you have a licence, check that your key is valid for 3.x (see Licence keys below).
What behaves differently on Android
- Initialisation runs in the background. The Next-Gen SDK must finish initialising before it can load ads. The plugin starts it inside
Initon a background thread, and anyLoad*,SetAppMuted,SetAppVolumeorOpenAdInspectorcall made in the meantime is queued and runs as soon as the SDK is ready, so loading an ad right afterInitkeeps working. - Error codes are unchanged, the domain is not.
MTEventArgs.ErrorCodekeeps Google's numeric codes (2= network error,3= no fill, ...).ErrorDomainis nowcom.google.android.libraries.ads.mobile.sdk. - Interstitials ignore the back key, as the Next-Gen SDK does. Users close them with the close button.
- Anchored adaptive banners (
AnchoredAdaptive, andSmart, which the Next-Gen SDK removed) use Google's large anchored adaptive size, which can be slightly taller. - Banner refreshes raise
AdsLoadedagain, and the banner's height follows the new ad. - Child-directed and under-age tags are mapped to the Next-Gen age treatment as Google documents: if either is set, the request gets the child treatment.
- With AdMob mediation, the first request can go out before every adapter has initialised. The Next-Gen SDK can take up to 30 seconds to report adapters, so the plugin does not wait for it, as Google's own sample does not.
New in 3.0
OnAdPaid: impression-level ad revenue for every format (format, ad unit, value, currency, precision and ad source), to forward to Firebase or your attribution SDK. Enable impression-level ad revenue in the AdMob UI for the values to be filled in.OpenAdInspector()andOnAdInspectorClosed: open Google's Ad Inspector on a test device.PublisherPrivacyPersonalizationState,ContentUrl,NeighboringContentUrlsandCustomTargeting.- Native ads on Android now resize to their template, so their pieces no longer spill outside the native ad view.
Licence keys
A 1 Major Version licence is valid for the major version it was bought for, and covers every minor update of that version. A key bought for 2.x keeps working on every 2.x release, but it does not unlock the licensed features on 3.x: for those you need a 3.x key, or you can keep using 2.x.
A perpetual licence (Single Supporter and Multi Supporter) is valid for all versions, including 3.x.
If your key is not valid for the version you are using, the plugin logs "The key was issued for version ... of the plugin and is not valid for version ..." and runs as the free version. You can buy a licence here.
Initialization
Setting up the plugin takes four steps: install the package, register it in MauiProgram, add your AdMob App ID to each platform and call Init once per platform.
1. Install the package
Add Plugin.MauiMTAdmob to your MAUI project. Version 2.5.0 and later target .NET 10. If you are still on .NET 9, use version 2.4.0.
Version 3.x runs Android on Google's Mobile Ads Next-Gen SDK and requires Android 7.0 (API 24) or later. Set it in your app's csproj:
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
If your app needs an older Android version, use version 2.x. See the Version 3 (Next-Gen) section.
2. Register the plugin
In your MauiProgram class, add UseMauiMTAdmob() to the builder:
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiMTAdmob();
3. Add your App Id
Android
Add your AdMob APPLICATION_ID to Platforms/Android/AndroidManifest.xml, inside the <application> tag:
<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="YOUR APPLICATION ID" />
iOS
Add these keys to Platforms/iOS/Info.plist:
<key>GADApplicationIdentifier</key>
<string>YOUR APPLICATION ID</string>
<key>GADIsAdManagerApp</key>
<true/>
<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads to you.</string>
NSUserTrackingUsageDescription is the text shown in the App Tracking Transparency dialog. Without it, iOS never shows the dialog and tracking is always denied. See the Consent & UMP section for details.
4. Call Init
Everything in this guide is reached through CrossMauiMTAdmob.Current. Add using Plugin.MauiMtAdmob; (and using Plugin.MauiMtAdmob.Extra; for the enums) to the files that use it.
Android (MainActivity.cs)
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Free version
CrossMauiMTAdmob.Current.Init(this, "ca-app-pub-xxxxxxxxxxxxxxxx~yyyyyyyyyy");
// Licensed version
// CrossMauiMTAdmob.Current.Init(this, "YOUR APP ID", license: "YOUR LICENSE KEY");
}
Calling Init again when Android recreates the activity is safe: the plugin keeps the ads it has already loaded and simply switches to the new activity.
iOS (AppDelegate.cs)
public AppDelegate()
{
// Free version
CrossMauiMTAdmob.Current.Init();
// Licensed version
// CrossMauiMTAdmob.Current.Init(license: "YOUR LICENSE KEY");
}
Android Method Signature
void Init(MauiAppCompatActivity activity, string appId, string? license = null, string? nativeAdsId = null, string? openAdsId = null, bool enableOpenAds = false, bool tagForUnderAgeOfConsent = false, string? testDeviceId = null, bool forceTesting = false, DebugGeography geography = DebugGeography.DEBUG_GEOGRAPHY_DISABLED, bool initialiseConsentAtStartup = true, bool debugMode = false);
| Parameter | Type | Description | Default |
|---|---|---|---|
| activity | MauiAppCompatActivity | Your MainActivity. | N/A |
| appId | string | Your AdMob App ID (the one with the ~). | N/A |
| license | string | Your license key. Leave it empty to use the free version. | null |
| nativeAdsId | string | The ad unit ID used for Native Ads. | null |
| openAdsId | string | The ad unit ID used for App Open Ads. | null |
| enableOpenAds | bool | True to show App Open Ads (licensed version). | false |
| tagForUnderAgeOfConsent | bool | True if the user is under the age of consent. The consent form will not be shown. | false |
| testDeviceId | string | The ID of your test device, used to test the consent form. You can find it in the log the first time you request consent. | null |
| forceTesting | bool | True to force the consent testing settings (geography and test device). | false |
| geography | DebugGeography | Pretends the device is in a specific region to test the consent form. | DEBUG_GEOGRAPHY_DISABLED |
| initialiseConsentAtStartup | bool | True to request consent as soon as the plugin starts. Set it to false to ask for it later with InitialiseAndShowConsentForm(). | true |
| debugMode | bool | If true, the plugin writes debug messages to the console. | false |
iOS Method Signature
void Init(string? license = null, string? nativeAdsId = null, string? openAdsId = null, bool enableOpenAds = false, bool tagForUnderAgeOfConsent = false, string[]? testDeviceIds = null, DebugGeography geography = DebugGeography.DEBUG_GEOGRAPHY_DISABLED, bool initialiseConsentAtStartup = true, bool debugMode = false, bool handleTrackingAuthorization = true);
| Parameter | Type | Description | Default |
|---|---|---|---|
| license | string | Your license key. Leave it empty to use the free version. | null |
| nativeAdsId | string | The ad unit ID used for Native Ads. | null |
| openAdsId | string | The ad unit ID used for App Open Ads. | null |
| enableOpenAds | bool | True to show App Open Ads (licensed version). | false |
| tagForUnderAgeOfConsent | bool | True if the user is under the age of consent. The consent form will not be shown. | false |
| testDeviceIds | string[] | The IDs of your test devices, used to test the consent form. | null |
| geography | DebugGeography | Pretends the device is in a specific region to test the consent form. | DEBUG_GEOGRAPHY_DISABLED |
| initialiseConsentAtStartup | bool | True to request consent as soon as the plugin starts. Set it to false to ask for it later with InitialiseAndShowConsentForm(). | true |
| debugMode | bool | If true, the plugin writes debug messages to the console. | false |
| handleTrackingAuthorization | bool | If true, the plugin shows the App Tracking Transparency dialog after the consent form. Set it to false if your app asks for it on its own. | true |
DebugGeography values
DEBUG_GEOGRAPHY_DISABLED: uses the real location of the device.DEBUG_GEOGRAPHY_EEA: behaves as if the device is in the EEA or UK.DEBUG_GEOGRAPHY_NOT_EEA: behaves as if the device is outside the EEA.DEBUG_GEOGRAPHY_REGULATED_US_STATE: behaves as if the device is in a regulated US state.DEBUG_GEOGRAPHY_OTHER: behaves as if the device is in any other region.
Debug geography only works on test devices. Remember to remove it before you publish your app.
Please note:
You don't need to call:
MobileAds.Initialize(this); (Android)
or
MobileAds.SharedInstance.Start(CompletionHandler); (iOS)
The plugin will do it for you! If you need to know when the Mobile Ads SDK is ready, subscribe to OnMobileAdsInitialized.
Global Settings
These properties and methods apply to the whole plugin. Set them through CrossMauiMTAdmob.Current, usually right after Init:
CrossMauiMTAdmob.Current.TestDevices = new() { "YOUR TEST DEVICE ID" };
CrossMauiMTAdmob.Current.MaxAdContentRating = MTMaxAdContentRating.MaxAdContentRatingPg;
General
| Property | Description |
|---|---|
IsEnabled | Set it to false to stop loading and showing every ad, for example after the user buys your ad-free version. |
DebugMode | If true, the plugin writes debug messages to the console. |
IsPluginInitialised | True once Init has been called. |
IsLicensed | True if a valid license key has been passed to Init. |
CanLoadBanners | True when banners can start loading (after the SDK is ready and, in the licensed version, consent has been handled). |
BannerAdsId | The ad unit ID used by banners that don't set their own AdsId. |
DefaultBannerMode | The collapsible mode used by banners when they load by themselves (licensed version). |
MaximumNumberOfAdsCached | The maximum number of full-screen ads of each type kept loaded at the same time (licensed version). 0 means no limit. |
AreOpenAdsEnabled / MinutesBetweenOpenAds | See App Open Ads. |
UseAdManagerForNativeAds / AdChoicesCorner | See Native Ads. |
Ad requests and privacy
| Property | Description |
|---|---|
TestDevices | The IDs of your test devices. Test devices always receive test ads, so you can't click your own ads by mistake. |
UserPersonalizedAds | Set it to false to request non-personalised ads. |
UseRestrictedDataProcessing | Enables restricted data processing, for compliance with the CCPA. |
ComplyWithFamilyPolicies | Set it to true for apps aimed at families: banners will only request ads rated G. Currently used on iOS only. On Android, set MaxAdContentRating and TagForChildDirectedTreatment yourself. |
TagForChildDirectedTreatment | Marks requests as directed at children (COPPA): TagForChildDirectedTreatmentTrue, ...False or ...Unspecified. |
TagForUnderAgeOfConsent | Marks requests as coming from users under the age of consent (GDPR): TagForUnderAgeOfConsentTrue, ...False or ...Unspecified. |
MaxAdContentRating | The highest content rating of the ads you accept: MaxAdContentRatingG, ...Pg, ...T, ...Ma or ...Unspecified. |
PublisherPrivacyPersonalizationState | Google's publisher privacy treatment: Default, Enabled or Disabled. Disabled turns off ad personalization for every request, whatever the other signals say. |
Content mapping and targeting
These values are sent with every ad request (banners, full-screen ads and native ads), so change them when the user moves to different content.
| Property | Description |
|---|---|
ContentUrl | The URL of the content the user is viewing, so Google can serve ads relevant to it. |
NeighboringContentUrls | URLs of other content shown on the same screen. Use it when a screen shows several pieces of content. |
CustomTargeting | Key-value pairs used by Google Ad Manager line items. AdMob ignores them. |
CrossMauiMTAdmob.Current.ContentUrl = "https://www.example.com/recipes/carbonara";
CrossMauiMTAdmob.Current.NeighboringContentUrls = new() { "https://www.example.com/recipes/amatriciana" };
CrossMauiMTAdmob.Current.CustomTargeting = new() { { "section", "recipes" } };
Methods
| Method | Description |
|---|---|
SetAppMuted(bool muted) | Tells the SDK that your app is muted, so video ads start without sound. |
SetAppVolume(float volume) | Tells the SDK your app's volume, from 0 to 1. |
GetSdkVersion() | Returns the version of the Google Mobile Ads SDK in use. |
GetAdContentRatingString() | Returns the current MaxAdContentRating as the string used by the SDK. |
OpenAdInspector() | Opens Google's Ad Inspector, an in-app overlay to debug your ad requests and mediation. See Ad Inspector below. |
Events
OnMobileAdsInitialized: the Google Mobile Ads SDK has finished starting.OnAdPaid: an ad has earned revenue. See Impression-level ad revenue below.OnAdInspectorClosed: the Ad Inspector has been closed, or could not be opened.
Impression-level ad revenue
OnAdPaid is raised every time an ad earns money, for every format: banners, interstitials, rewarded, rewarded interstitials, app open ads and native ads. Forward it to your analytics or attribution SDK, for example as Firebase's ad_impression event.
CrossMauiMTAdmob.Current.OnAdPaid += (sender, e) =>
{
// e.Value is in units of e.CurrencyCode (e.ValueMicros is the raw value in micros)
Debug.WriteLine($"{e.AdFormat} {e.AdUnitId}: {e.Value} {e.CurrencyCode} ({e.Precision})");
};
| MTPaidEventArgs | Description |
|---|---|
AdFormat | Banner, Interstitial, Rewarded, RewardedInterstitial, AppOpen or Native. |
AdUnitId | The ad unit the ad was loaded from. |
ValueMicros / Value | The value of the ad, in micros and in units of CurrencyCode. |
CurrencyCode | The ISO 4217 currency code of the value. |
Precision | Unknown, Estimated, PublisherProvided or Precise. |
AdSourceName | The ad network that served the ad, when known. |
ResponseId | The ID of the ad response, useful when reporting an issue to Google. |
Impression-level ad revenue must be turned on in your AdMob account settings, otherwise the values are not populated.
Ad Inspector
The Ad Inspector shows every ad request made by your app, its result, and the mediation waterfall. It only opens on test devices (see TestDevices) or on devices where you enabled it from the AdMob UI.
CrossMauiMTAdmob.Current.OnAdInspectorClosed += (sender, e) =>
{
if (e.ErrorCode != null)
Debug.WriteLine($"Ad Inspector error: {e.ErrorMessage}");
};
CrossMauiMTAdmob.Current.OpenAdInspector();
Banners
To add a Banner to a page you have two options:
XAML
<controls:MTAdView x:Name="myAds" AdsId="YOUR BANNER ID" AdSize="AnchoredAdaptive" />
Remember to add this namespace to your XAML page:
xmlns:controls="clr-namespace:Plugin.MauiMtAdmob.Controls;assembly=Plugin.MauiMtAdmob"
Code
MTAdView ads = new MTAdView
{
AdsId = "YOUR BANNER ID",
AdSize = BannerSize.AnchoredAdaptive
};
Now you can add the control to your layout. The banner loads by itself as soon as the plugin is ready (and, in the licensed version, once consent has been handled), so you don't need to call anything else.
IMPORTANT
During development, always use the test Banner IDs. Remember to replace them with your own IDs before you publish:
Android: ca-app-pub-3940256099942544/6300978111
iOS: ca-app-pub-3940256099942544/2934735716
Properties
| Property | Description |
|---|---|
AdsId | The ad unit ID of the banner. If empty, the global BannerAdsId is used. |
AdSize | The size of the banner. See the values below. |
BannerSize values
Banner: 320x50LargeBanner: 320x100MediumRectangle: 300x250FullBanner: 468x60Leaderboard: 728x90AnchoredAdaptive: full width, with the best height for the device. This is the size Google recommends for banners at the top or bottom of the screen.InlineAdaptive: full width, for banners placed inside scrolling content.Smart: kept for compatibility with older apps. UseAnchoredAdaptiveinstead.
Reloading a banner
You can ask a banner to load a new ad at any time:
myAds.LoadAd();
Collapsible Banners (Licensed version only)
A collapsible banner first appears larger and then collapses to its normal size. To show one, call:
myAds.LoadAd(CollapsibleBannerMode.Top);
CollapsibleBannerMode can be None, Top or Bottom. Use Top for a banner at the top of the page and Bottom for one at the bottom.
To make every banner collapsible without calling LoadAd, set CrossMauiMTAdmob.Current.DefaultBannerMode before the banners are created.
Events
Each banner raises its own events:
AdsLoaded: the banner has loaded an ad.AdsFailedToLoad: the banner failed to load.MTEventArgscontainsErrorCode,ErrorMessageandErrorDomain.AdsImpression: an impression has been recorded.AdsClicked: the user clicked the banner.AdsOpened: the banner opened an overlay.AdsClosed: the overlay has been closed.AdsLeftApplication: the user left the app after clicking the banner.
myAds.AdsLoaded += (sender, args) => myAds.IsVisible = true;
myAds.AdsFailedToLoad += (sender, args) =>
{
myAds.IsVisible = false;
Console.WriteLine($"Banner error {args.ErrorCode}: {args.ErrorMessage}");
};
Global properties
These properties apply to every banner. You can find the full list in the Global Settings section.
BannerAdsId: the ad unit ID used by banners that don't set their ownAdsId.DefaultBannerMode: the collapsible mode used when a banner loads by itself.UserPersonalizedAds: set it to false to request non-personalized ads.UseRestrictedDataProcessing: for compliance with the CCPA.TestDevices: the IDs of your test devices.
CrossMauiMTAdmob.Current.BannerAdsId = "YOUR BANNER ID";
CrossMauiMTAdmob.Current.UserPersonalizedAds = true;
Interstitials
Interstitials are full-screen ads that cover your app. Show them at natural breaks, such as between levels or after a task is complete. This section shows how to use them with CrossMauiMTAdmob.Current.
Test IDs: Android: ca-app-pub-3940256099942544/1033173712 and iOS: ca-app-pub-3940256099942544/4411468910
Loading an Interstitial Ad
To load an interstitial ad, use the LoadInterstitial method with the specified ad unit:
void LoadInterstitial(string adUnit);
Example:
CrossMauiMTAdmob.Current.LoadInterstitial("your-ad-unit-id");
Checking if an Interstitial Ad is Loaded
To check if an interstitial ad is loaded, use the IsInterstitialLoaded method:
bool IsInterstitialLoaded();
Example:
bool isLoaded = CrossMauiMTAdmob.Current.IsInterstitialLoaded();
if (isLoaded) {
// Interstitial ad is loaded
}
Showing an Interstitial Ad
To show the loaded interstitial ad, use the ShowInterstitial method:
void ShowInterstitial();
Example:
if (CrossMauiMTAdmob.Current.IsInterstitialLoaded()) {
CrossMauiMTAdmob.Current.ShowInterstitial();
}
Handling Interstitial Ad Events
The plugin provides several events to handle interstitial ad lifecycle events:
event EventHandler OnInterstitialLoaded: Raised when an interstitial ad is loaded.event EventHandler<MTEventArgs> OnInterstitialFailedToLoad: Raised when an interstitial ad fails to load.event EventHandler OnInterstitialOpened: Raised when an interstitial ad is opened.event EventHandler OnInterstitialClosed: Raised when an interstitial ad is closed.event EventHandler<MTEventArgs> OnInterstitialFailedToShow: Raised when an interstitial ad fails to show.event EventHandler OnInterstitialImpression: Raised when an interstitial ad impression occurs.event EventHandler OnInterstitialClicked: Raised when an interstitial ad is clicked.
Example:
CrossMauiMTAdmob.Current.OnInterstitialLoaded += (sender, args) => {
// Handle interstitial ad loaded
};
CrossMauiMTAdmob.Current.OnInterstitialFailedToLoad += (sender, args) => {
// Handle interstitial ad failed to load
};
CrossMauiMTAdmob.Current.OnInterstitialOpened += (sender, args) => {
// Handle interstitial ad opened
};
CrossMauiMTAdmob.Current.OnInterstitialClosed += (sender, args) => {
// Handle interstitial ad closed
};
CrossMauiMTAdmob.Current.OnInterstitialFailedToShow += (sender, args) => {
// Handle interstitial ad failed to show
};
CrossMauiMTAdmob.Current.OnInterstitialImpression += (sender, args) => {
// Handle interstitial ad impression
};
CrossMauiMTAdmob.Current.OnInterstitialClicked += (sender, args) => {
// Handle interstitial ad clicked
};
Getting the Number of Interstitials Loaded
To get the number of interstitials loaded, use the GetNumberOfInterstitialsLoaded method:
int GetNumberOfInterstitialsLoaded();
Example:
int numberOfInterstitials = CrossMauiMTAdmob.Current.GetNumberOfInterstitialsLoaded();
Only the licensed version can keep more than one interstitial loaded at a time. The free version keeps one and waits until it has been shown before it loads another. Use MaximumNumberOfAdsCached to limit how many interstitials are kept.
A good pattern is to load the next interstitial in OnInterstitialClosed, so one is always ready when you need it.
Emptying the Interstitial Ads List
To empty the interstitial ads list, use the EmptyInterstitialAdsList method:
void EmptyInterstitialAdsList();
Example:
CrossMauiMTAdmob.Current.EmptyInterstitialAdsList();
Use this method to clear the list of loaded interstitial ads. Normally you don't need to use it as the library automatically removes loaded ads that are too old (1 hour) as they might be not valid anymore.
Rewarded Ads
Rewarded ads give your users something in return (coins, an extra life, premium content...) for watching an ad. This section shows how to use them with CrossMauiMTAdmob.Current.
Test IDs: Android: ca-app-pub-3940256099942544/5224354917 and iOS: ca-app-pub-3940256099942544/1712485313
Loading a Rewarded Ad
void LoadRewarded(string adUnit, MTRewardedAdOptions? options = null, string queueId = "default");
Example:
CrossMauiMTAdmob.Current.LoadRewarded("your-ad-unit-id");
Checking if a Rewarded Ad is Loaded
bool IsRewardedLoaded(string queueId = "default");
Showing a Rewarded Ad
void ShowRewarded(string queueId = "default");
Example:
if (CrossMauiMTAdmob.Current.IsRewardedLoaded())
{
CrossMauiMTAdmob.Current.ShowRewarded();
}
Giving the reward
When the user has watched the ad, the plugin raises OnUserEarnedReward. The reward type and amount you set in AdMob are in MTEventArgs:
CrossMauiMTAdmob.Current.OnUserEarnedReward += (sender, args) =>
{
// args.RewardType (e.g. "coins") and args.RewardAmount (e.g. 10)
AddCoins(args.RewardAmount);
};
Only give the reward in this event: if the user closes the ad early, OnRewardedClosed is raised but OnUserEarnedReward is not.
Server-side verification
If you use server-side verification, pass an MTRewardedAdOptions when you load the ad. Its values are sent to your callback URL:
var options = new MTRewardedAdOptions
{
UserId = "user-123",
CustomData = "level-5"
};
CrossMauiMTAdmob.Current.LoadRewarded("your-ad-unit-id", options);
Using more than one queue
Loaded ads are kept in a queue. If you use different ad units in different parts of your app, you can give each one its own queue so the right ad is shown in the right place:
CrossMauiMTAdmob.Current.LoadRewarded("shop-ad-unit-id", queueId: "shop");
CrossMauiMTAdmob.Current.LoadRewarded("level-ad-unit-id", queueId: "levels");
if (CrossMauiMTAdmob.Current.IsRewardedLoaded("shop"))
{
CrossMauiMTAdmob.Current.ShowRewarded("shop");
}
When you don't pass a queueId, the "default" queue is used.
Handling Rewarded Ad Events
OnRewardedLoaded: a rewarded ad has loaded.OnRewardedFailedToLoad: a rewarded ad failed to load (MTEventArgscontains the error).OnRewardedOpened: the ad is now on screen.OnRewardedClosed: the ad has been closed. This is a good place to load the next one.OnRewardedFailedToShow: the ad could not be shown (MTEventArgscontains the error).OnRewardedImpression: an impression has been recorded.OnRewardedClicked: the user clicked the ad.OnUserEarnedReward: the user has earned the reward.
CrossMauiMTAdmob.Current.OnRewardedClosed += (sender, args) =>
{
CrossMauiMTAdmob.Current.LoadRewarded("your-ad-unit-id");
};
CrossMauiMTAdmob.Current.OnRewardedFailedToLoad += (sender, args) =>
{
Console.WriteLine($"Rewarded error {args.ErrorCode}: {args.ErrorMessage}");
};
Getting the Number of Rewarded Ads Loaded
int GetNumberOfRewardedLoaded(string queueId = "default");
Only the licensed version can keep more than one ad loaded at a time. The free version keeps one rewarded ad at a time and waits until it has been shown before it loads another. Use MaximumNumberOfAdsCached to limit how many ads each queue keeps.
Emptying the Rewarded Ads List
CrossMauiMTAdmob.Current.EmptyRewardedAdsList(); // or EmptyRewardedAdsList("shop")
Normally you don't need it: the plugin automatically removes ads loaded more than 1 hour ago, because they may no longer be valid.
Rewarded Interstitial Ads
Rewarded interstitials reward the user like rewarded ads, but they appear on their own during natural breaks in your app (between levels, for example) without the user having to opt in first. Google asks you to show an intro screen that tells the user about the reward and lets them skip the ad.
Test IDs: Android: ca-app-pub-3940256099942544/5354046379 and iOS: ca-app-pub-3940256099942544/6978759866
Loading a Rewarded Interstitial Ad
void LoadRewardInterstitial(string adUnit, MTRewardedAdOptions? options = null, string queueId = "default");
Example:
CrossMauiMTAdmob.Current.LoadRewardInterstitial("your-ad-unit-id");
Checking if a Rewarded Interstitial Ad is Loaded
bool IsRewardInterstitialLoaded(string queueId = "default");
Showing a Rewarded Interstitial Ad
void ShowRewardInterstitial(string queueId = "default");
Example:
if (CrossMauiMTAdmob.Current.IsRewardInterstitialLoaded())
{
CrossMauiMTAdmob.Current.ShowRewardInterstitial();
}
Events
Rewarded interstitials use the same events as rewarded ads: OnRewardedLoaded, OnRewardedFailedToLoad, OnRewardedOpened, OnRewardedClosed, OnRewardedFailedToShow, OnRewardedImpression, OnRewardedClicked and OnUserEarnedReward. See the Rewarded section for details and examples.
CrossMauiMTAdmob.Current.OnUserEarnedReward += (sender, args) =>
{
// args.RewardType and args.RewardAmount
};
If you use rewarded ads and rewarded interstitials at the same time, keep track of which one you showed last so you know which one an event refers to.
Options and queues
MTRewardedAdOptions (for server-side verification) and queueId work exactly as they do for rewarded ads.
Getting the Number of Rewarded Interstitial Ads Loaded
int GetNumberOfRewardedInterstitialsLoaded(string queueId = "default");
Only the licensed version can keep more than one rewarded interstitial loaded at a time.
Emptying the Rewarded Interstitial Ads List
CrossMauiMTAdmob.Current.EmptyRewardInterstitialAdsList();
Normally you don't need it: the plugin automatically removes ads loaded more than 1 hour ago, because they may no longer be valid.
App Open Ads
App Open Ads are only available in the licensed version of the plugin. To enable them in your apps, you can buy the license here.
App Open Ads appear when users open your app or bring it back from the background. The plugin loads them, shows them at the right moment and handles errors for you, on both Android and iOS.
Test IDs: Android: ca-app-pub-3940256099942544/9257395921 and iOS: ca-app-pub-3940256099942544/5575463023
1. Enable them in Init
Set enableOpenAds to true and pass your App Open ad unit ID in openAdsId:
// Android
CrossMauiMTAdmob.Current.Init(this, "YOUR APP ID", license: "YOUR LICENSE KEY", openAdsId: "YOUR OPEN AD ID", enableOpenAds: true);
// iOS
CrossMauiMTAdmob.Current.Init(license: "YOUR LICENSE KEY", openAdsId: "YOUR OPEN AD ID", enableOpenAds: true);
2. Tell the plugin when the app comes back
To show an App Open Ad when the app returns to the foreground, call OnResume():
Android (MainActivity.cs)
protected override void OnResume()
{
base.OnResume();
CrossMauiMTAdmob.Current.OnResume();
}
iOS (AppDelegate.cs)
public override void OnActivated(UIApplication application)
{
base.OnActivated(application);
CrossMauiMTAdmob.Current.OnResume();
}
Settings
AreOpenAdsEnabled: turns App Open Ads on or off at runtime. For example, set it to false for users who bought your premium version.MinutesBetweenOpenAds: the minimum number of minutes between two App Open Ads. The default is 30, so users who switch apps often don't see an ad every time.
CrossMauiMTAdmob.Current.MinutesBetweenOpenAds = 60;
An App Open Ad loaded more than 4 hours ago is discarded and a fresh one is loaded, as Google recommends.
Events
OnAppOpenAdLoaded: an App Open Ad has loaded.OnAppOpenFailedToLoad: an App Open Ad failed to load (MTEventArgscontains the error).OnAppOpenOpened: the ad is now on screen.OnAppOpenClosed: the ad has been closed.OnAppOpenFailedToShow: the ad could not be shown.OnAppOpenImpression: an impression has been recorded.OnAppOpenClicked: the user clicked the ad.
Native Ads
Native Ads are only available in the licensed version of the plugin. You can buy the license here.
Native ads are made of separate pieces (headline, image, icon, call-to-action button...) that you lay out yourself, so the ad matches the look of your app.
Test IDs: Android: ca-app-pub-3940256099942544/2247696110 and iOS: ca-app-pub-3940256099942544/3986624511
1. Set the ad unit ID
Pass your native ad unit ID in the nativeAdsId parameter of Init:
CrossMauiMTAdmob.Current.Init(this, "YOUR APP ID", license: "YOUR LICENSE KEY", nativeAdsId: "YOUR NATIVE AD ID");
2. Create a template
A native ad is drawn with a ControlTemplate. The plugin finds each piece by its AutomationId, so you can arrange them however you like:
<ContentPage.Resources>
<ControlTemplate x:Key="NativeAdTemplate">
<Border>
<Grid>
<VerticalStackLayout>
<controls:MTMediaView AutomationId="Media" HeightRequest="150" MinimumWidthRequest="120" />
<Label AutomationId="Headline" FontAttributes="Bold" />
<Label AutomationId="Body" />
<Image AutomationId="Icon" />
<controls:MTRatingView AutomationId="StarRating" />
<Label AutomationId="Store" />
<Label AutomationId="Price" />
<Label AutomationId="Advertiser" />
<Button AutomationId="CallToAction" />
</VerticalStackLayout>
<Label AutomationId="Attribution" Text="Ad" BackgroundColor="#FFCC66"
HorizontalOptions="Start" VerticalOptions="Start" />
</Grid>
</Border>
</ControlTemplate>
</ContentPage.Resources>
| AutomationId | Control | Content |
|---|---|---|
Media | MTMediaView | The main image or video. |
Headline | Label | The title of the ad (always present). |
Body | Label | The description. |
Icon | Image | The advertiser's icon. |
Image | Image | A static image, if you prefer it to the media view. |
StarRating | MTRatingView | The app rating, for app install ads. |
Store | Label | The store, for app install ads. |
Price | Label | The price, for app install ads. |
Advertiser | Label | The advertiser's name. |
CallToAction | Button | The action button ("Install", "Open"...). |
Attribution | Label | The "Ad" label. Google requires native ads to be clearly marked as ads. |
Not every ad contains every piece. Leave out the ones you don't need.
Give the media view at least 120x120. Google requires it for video ads, and the native ad validator that appears on test devices reports anything smaller ("MediaView is too small for video"). In a Grid template, avoid * rows around the media view: they share the space left and can shrink it below the size it asks for. Use Auto rows instead.
3. Add the view to your page
<controls:MTNativeAdView x:Name="myNativeAd" ContentTemplate="{StaticResource NativeAdTemplate}" />
4. Load and show the ad
Load an ad with LoadNativeAd, then show it in your view when OnNativeAdLoaded is raised:
CrossMauiMTAdmob.Current.OnNativeAdLoaded += (sender, args) =>
{
MainThread.BeginInvokeOnMainThread(() => myNativeAd.ShowNativeAd());
};
CrossMauiMTAdmob.Current.LoadNativeAd();
To load several native ads in one request (up to 5), use LoadNativeAds:
CrossMauiMTAdmob.Current.LoadNativeAds(5);
Each call to ShowNativeAd() on a view uses the next loaded ad.
Settings
AdChoicesCorner: the corner where the AdChoices icon appears (ADCHOICES_TOP_LEFT,ADCHOICES_TOP_RIGHT,ADCHOICES_BOTTOM_RIGHT,ADCHOICES_BOTTOM_LEFT).UseAdManagerForNativeAds: set it to true to request native ads through Google Ad Manager instead of AdMob.
CrossMauiMTAdmob.Current.AdChoicesCorner = AdChoicesCorner.ADCHOICES_TOP_RIGHT;
Handling Native Ad Events
OnNativeAdLoaded: a native ad has loaded.OnNativeAdFailedToLoad: a native ad failed to load (MTEventArgscontains the error).OnNativeAdOpened: the ad opened an overlay.OnNativeAdClosed: the overlay has been closed.OnNativeAdFailedToShow: the ad could not be shown.OnNativeAdImpression: an impression has been recorded.OnNativeAdClicked: the user clicked the ad.
Google Ad Manager
If your ad units come from Google Ad Manager instead of AdMob, use the Ad Manager versions of the controls and methods. They work like the AdMob ones and raise the same events.
On iOS, remember to set GADIsAdManagerApp to true in your Info.plist (see Initialization).
Banners
Use AdMMTAdView instead of MTAdView. It has the same properties and events (AdsId, AdSize, AdsLoaded...), plus a BannerMode property to make it collapsible straight from XAML (collapsible banners need the licensed version):
<controls:AdMMTAdView x:Name="myAds"
AdsId="/21775744923/example/adaptive-banner"
AdSize="AnchoredAdaptive"
BannerMode="Bottom" />
Interstitials
CrossMauiMTAdmob.Current.LoadAdManagerInterstitial("/21775744923/example/interstitial");
if (CrossMauiMTAdmob.Current.IsAdManagerInterstitialLoaded())
{
CrossMauiMTAdmob.Current.ShowAdManagerInterstitial();
}
Also available: GetNumberOfAdManagerInterstitialsLoaded() and EmptyAdManagerInterstitialAdsList(). Events: the same OnInterstitial... events as AdMob interstitials.
Rewarded
CrossMauiMTAdmob.Current.LoadAdManagerRewarded("/21775744923/example/rewarded");
if (CrossMauiMTAdmob.Current.IsAdManagerRewardedLoaded())
{
CrossMauiMTAdmob.Current.ShowAdManagerRewarded();
}
Also available: GetNumberOfAdManagerRewardedLoaded() and EmptyAdManagerRewardedAdsList(). You can pass an MTRewardedAdOptions to LoadAdManagerRewarded for server-side verification. Events: the same OnRewarded... events and OnUserEarnedReward.
Rewarded Interstitials
CrossMauiMTAdmob.Current.LoadAdManagerRewardInterstitial("/21775744923/example/rewarded-interstitial");
if (CrossMauiMTAdmob.Current.IsAdManagerRewardInterstitialLoaded())
{
CrossMauiMTAdmob.Current.ShowAdManagerRewardInterstitial();
}
Also available: GetNumberOfAdManagerRewardInterstitialsLoaded() and EmptyAdManagerRewardInterstitialAdsList().
Native Ads
Set UseAdManagerForNativeAds to true and pass your Ad Manager native ad unit in nativeAdsId. Everything else works as described in the Native Ads section.
CrossMauiMTAdmob.Current.UseAdManagerForNativeAds = true;
Unlike the AdMob methods, the Ad Manager methods don't take a queueId.
Consent and UMP
Under the Google EU User Consent Policy, you must make certain disclosures to your users in the European Economic Area (EEA) along with the UK and obtain their consent to use cookies or other local storage, where legally required, and to use personal data (such as AdID) to serve ads. The licensed version of the plugin uses Google's User Messaging Platform (UMP) to ask for consent automatically, only from the users who need to give it.
To enable this feature in your apps, you can buy the license here.
Requesting User Consent
By default, consent is requested when you call Init. Ads start loading once the user has answered.
If you prefer to ask later (for example, after your onboarding screens), set initialiseConsentAtStartup to false in Init and call InitialiseAndShowConsentForm when you are ready:
CrossMauiMTAdmob.Current.InitialiseAndShowConsentForm();
If your app is only published in countries where consent is not required, you can skip it completely:
CrossMauiMTAdmob.Current.SkipConsent = true; // set it before Init
Privacy options
Google requires apps to let users change their consent choice at any time. Check PrivacyOptionStatus and, if it is Required, show a button (for example in your settings page) that opens the privacy options form:
privacyButton.IsVisible = CrossMauiMTAdmob.Current.PrivacyOptionStatus == PrivacyOptionStatus.Required;
void OnPrivacyClicked(object sender, EventArgs e)
{
CrossMauiMTAdmob.Current.ShowPrivacyOptionsForm();
}
PrivacyOptionStatus is updated after the consent information has been loaded, so check it after OnConsentInfoUpdateSuccess or OnConsentFormDismissed.
Checking User Consent Status
ConsentStatus? status = CrossMauiMTAdmob.Current.GetConsentStatus();
Unknown: consent has not been requested yet.NotRequired: the user is not in a region where consent is needed.Required: consent is needed but the user has not answered yet.Obtained: the user has answered the consent form.
Obtained means the user answered, not that they agreed to everything. To know what you can show, use:
CanShowPersonalisedAds(): personalised ads are allowed.CanShowNonPersonalisedAds(): at least non-personalised ads are allowed.CanShowLimitedAds(): only limited ads are allowed.
These methods return null when the answer is not known yet. IsConsentInitialised tells you whether the consent flow has already run.
Testing the consent form
If you are not in the EEA or UK, the form will not appear by default. To test it:
- Run the app once with debugMode on and find your test device ID in the log.
- Pass it in testDeviceId (Android) or testDeviceIds (iOS) and set geography to
DEBUG_GEOGRAPHY_EEA. On Android, also set forceTesting to true. - Call
CrossMauiMTAdmob.Current.Reset()to clear the saved answer and see the form again.
Remove the test settings and never call Reset() in the version you publish.
App Tracking Transparency (iOS)
On iOS 14 and later, apps must ask permission before they use the advertising identifier. By default, the plugin shows the App Tracking Transparency dialog right after the consent form, in the order Google recommends.
Remember to add NSUserTrackingUsageDescription to your Info.plist (see Initialization). Without it, iOS never shows the dialog and tracking is always denied.
If you want to ask at a different moment, set handleTrackingAuthorization to false in Init and ask yourself:
bool authorized = await CrossMauiMTAdmob.Current.RequestTrackingAuthorizationAsync();
IsTrackingAuthorized and GetTrackingAuthorizationStatus() return the last known answer (null if it has not been asked yet). On Android these members do nothing.
Handling Consent Events
OnConsentInfoUpdateSuccess: the consent information has been updated.OnConsentInfoUpdateFailure: the consent information could not be updated (MTEventArgs.ErrorMessagecontains the reason).OnConsentFormLoadSuccess: the consent form has loaded.OnConsentFormLoadFailure: the consent form failed to load.OnConsentFormDismissed: the user has closed the consent form.
CrossMauiMTAdmob.Current.OnConsentFormDismissed += (sender, args) =>
{
if (args.ErrorMessage != null)
{
Console.WriteLine($"Consent error: {args.ErrorMessage}");
}
};
Consent not showing?
Turn on debug mode (CrossMauiMTAdmob.Current.DebugMode = true or debugMode: true in Init). The plugin then logs each step of the consent flow, prefixed with [MauiMTAdmob], so you can tell a form that was not required from one that failed. The consent form might not work on the iOS simulator: if it doesn't, try a real device.
Known issues and fix
"Xamarin.Google.iOS.UserMessagingPlatform" Version="1.1.0.1" to your iOS project.
Replace the line
<_GoogleUserMessagingPlatformAssemblyName> Google.UserMessagingPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
with
<_GoogleUserMessagingPlatformAssemblyName>Google.UserMessagingPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Xamarin.AndroidX.Lifecycle.LiveData Version="2.6.2.3"
"Xamarin.AndroidX.Activity" Version="1.8.2"
"Xamarin.AndroidX.Activity.Ktx" Version="1.8.2"
CrossMauiMTAdmob.Current.DebugMode = true or debugMode: true in Init). The plugin logs each step of the consent flow, prefixed with [MauiMTAdmob], so you can tell a form that was not required (for example because you are outside the EEA) from one that failed. Consent is only requested in the licensed version. To force the form while testing, see Testing the consent form in the Consent & UMP section.NSUserTrackingUsageDescription to your Info.plist. Without it, iOS never shows the dialog and tracking is always denied. Also check that you didn't set handleTrackingAuthorization to false in Init.<PropertyGroup Condition="$(TargetFramework.Contains('-ios'))">
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ForceSimulatorX64ArchitectureInIDE>true</ForceSimulatorX64ArchitectureInIDE>
</PropertyGroup>SupportedOSPlatformVersion to 24.0 for Android in your csproj, or stay on version 2.x if your app must support older Android versions.MTMediaView in your template at least 120x120, and avoid * rows around it in a Grid (use Auto), so the layout cannot shrink it.Links
VIDEO
Get Filthy Rich by Monetizing Your Xamarin.Forms App with AdMob - Gerald Versluis (Youtube)
LINKS
Available on Nuget: https://www.nuget.org/packages/Plugin.MauiMTAdmob
Tutorial: https://www.xamarinexpert.it/admob-made-easy/
To report any issue: https://github.com/marcojak/MauiMTAdmob/issues
DISCLAIMER OF LIABILITY
By using the MTAdmob plugin you agree that:
- The Plugin is provided "as is" without any warranties or guarantees of any kind, either expressed or implied.
- I shall not be responsible or liable for any direct, indirect, incidental, special, or consequential damages arising out of the use or inability to use the Plugin.
- I do not warrant that the Plugin will meet your specific requirements or operate without interruption.
You acknowledge that the use of the Plugin is at your own risk. The author of the plugin disclaims all liability for any damage to your computer system or loss of data that results from the use of the Plugin.
This disclaimer of liability is a fundamental part of the agreement between you and the author of the plugin. You agree that the limitations and exclusions of liability set forth in this disclaimer are reasonable and fair.
If you do not agree to these terms, do not use the Plugin.