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.x2.x
Android SDKGoogle Mobile Ads Next-Gen SDKLegacy Google Mobile Ads SDK
iOS SDKGoogle Mobile Ads SDK 12.xGoogle Mobile Ads SDK 12.x
Android minimum versionAndroid 7.0 (API 24)Android 5.0 (API 21)
Mediation on AndroidAdMob mediation onlyAny mediation platform
StatusCurrent versionStill 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 Init on a background thread, and any Load*, SetAppMuted, SetAppVolume or OpenAdInspector call made in the meantime is queued and runs as soon as the SDK is ready, so loading an ad right after Init keeps working.
  • Error codes are unchanged, the domain is not. MTEventArgs.ErrorCode keeps Google's numeric codes (2 = network error, 3 = no fill, ...). ErrorDomain is now com.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, and Smart, which the Next-Gen SDK removed) use Google's large anchored adaptive size, which can be slightly taller.
  • Banner refreshes raise AdsLoaded again, 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() and OnAdInspectorClosed: open Google's Ad Inspector on a test device.
  • PublisherPrivacyPersonalizationState, ContentUrl, NeighboringContentUrls and CustomTargeting.
  • 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
activityMauiAppCompatActivityYour MainActivity.N/A
appIdstringYour AdMob App ID (the one with the ~).N/A
licensestringYour license key. Leave it empty to use the free version.null
nativeAdsIdstringThe ad unit ID used for Native Ads.null
openAdsIdstringThe ad unit ID used for App Open Ads.null
enableOpenAdsboolTrue to show App Open Ads (licensed version).false
tagForUnderAgeOfConsentboolTrue if the user is under the age of consent. The consent form will not be shown.false
testDeviceIdstringThe 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
forceTestingboolTrue to force the consent testing settings (geography and test device).false
geographyDebugGeographyPretends the device is in a specific region to test the consent form.DEBUG_GEOGRAPHY_DISABLED
initialiseConsentAtStartupboolTrue to request consent as soon as the plugin starts. Set it to false to ask for it later with InitialiseAndShowConsentForm().true
debugModeboolIf 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
licensestringYour license key. Leave it empty to use the free version.null
nativeAdsIdstringThe ad unit ID used for Native Ads.null
openAdsIdstringThe ad unit ID used for App Open Ads.null
enableOpenAdsboolTrue to show App Open Ads (licensed version).false
tagForUnderAgeOfConsentboolTrue if the user is under the age of consent. The consent form will not be shown.false
testDeviceIdsstring[]The IDs of your test devices, used to test the consent form.null
geographyDebugGeographyPretends the device is in a specific region to test the consent form.DEBUG_GEOGRAPHY_DISABLED
initialiseConsentAtStartupboolTrue to request consent as soon as the plugin starts. Set it to false to ask for it later with InitialiseAndShowConsentForm().true
debugModeboolIf true, the plugin writes debug messages to the console.false
handleTrackingAuthorizationboolIf 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

PropertyDescription
IsEnabledSet it to false to stop loading and showing every ad, for example after the user buys your ad-free version.
DebugModeIf true, the plugin writes debug messages to the console.
IsPluginInitialisedTrue once Init has been called.
IsLicensedTrue if a valid license key has been passed to Init.
CanLoadBannersTrue when banners can start loading (after the SDK is ready and, in the licensed version, consent has been handled).
BannerAdsIdThe ad unit ID used by banners that don't set their own AdsId.
DefaultBannerModeThe collapsible mode used by banners when they load by themselves (licensed version).
MaximumNumberOfAdsCachedThe maximum number of full-screen ads of each type kept loaded at the same time (licensed version). 0 means no limit.
AreOpenAdsEnabled / MinutesBetweenOpenAdsSee App Open Ads.
UseAdManagerForNativeAds / AdChoicesCornerSee Native Ads.

Ad requests and privacy

PropertyDescription
TestDevicesThe IDs of your test devices. Test devices always receive test ads, so you can't click your own ads by mistake.
UserPersonalizedAdsSet it to false to request non-personalised ads.
UseRestrictedDataProcessingEnables restricted data processing, for compliance with the CCPA.
ComplyWithFamilyPoliciesSet 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.
TagForChildDirectedTreatmentMarks requests as directed at children (COPPA): TagForChildDirectedTreatmentTrue, ...False or ...Unspecified.
TagForUnderAgeOfConsentMarks requests as coming from users under the age of consent (GDPR): TagForUnderAgeOfConsentTrue, ...False or ...Unspecified.
MaxAdContentRatingThe highest content rating of the ads you accept: MaxAdContentRatingG, ...Pg, ...T, ...Ma or ...Unspecified.
PublisherPrivacyPersonalizationStateGoogle'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.

PropertyDescription
ContentUrlThe URL of the content the user is viewing, so Google can serve ads relevant to it.
NeighboringContentUrlsURLs of other content shown on the same screen. Use it when a screen shows several pieces of content.
CustomTargetingKey-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

MethodDescription
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})");
};
MTPaidEventArgsDescription
AdFormatBanner, Interstitial, Rewarded, RewardedInterstitial, AppOpen or Native.
AdUnitIdThe ad unit the ad was loaded from.
ValueMicros / ValueThe value of the ad, in micros and in units of CurrencyCode.
CurrencyCodeThe ISO 4217 currency code of the value.
PrecisionUnknown, Estimated, PublisherProvided or Precise.
AdSourceNameThe ad network that served the ad, when known.
ResponseIdThe 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

PropertyDescription
AdsIdThe ad unit ID of the banner. If empty, the global BannerAdsId is used.
AdSizeThe size of the banner. See the values below.

BannerSize values

  • Banner: 320x50
  • LargeBanner: 320x100
  • MediumRectangle: 300x250
  • FullBanner: 468x60
  • Leaderboard: 728x90
  • AnchoredAdaptive: 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. Use AnchoredAdaptive instead.

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. MTEventArgs contains ErrorCode, ErrorMessage and ErrorDomain.
  • 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 own AdsId.
  • 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 (MTEventArgs contains 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 (MTEventArgs contains 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 (MTEventArgs contains 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>
AutomationIdControlContent
MediaMTMediaViewThe main image or video.
HeadlineLabelThe title of the ad (always present).
BodyLabelThe description.
IconImageThe advertiser's icon.
ImageImageA static image, if you prefer it to the media view.
StarRatingMTRatingViewThe app rating, for app install ads.
StoreLabelThe store, for app install ads.
PriceLabelThe price, for app install ads.
AdvertiserLabelThe advertiser's name.
CallToActionButtonThe action button ("Install", "Open"...).
AttributionLabelThe "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 (MTEventArgs contains 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:

  1. Run the app once with debugMode on and find your test device ID in the log.
  2. Pass it in testDeviceId (Android) or testDeviceIds (iOS) and set geography to DEBUG_GEOGRAPHY_EEA. On Android, also set forceTesting to true.
  3. 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.ErrorMessage contains 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

iOS: Error: clang++ exited with code 1: Undefined symbols for architecture arm64: "OBJC_CLASS$_UMPConsentForm"...
If you get this error, simply add the package
"Xamarin.Google.iOS.UserMessagingPlatform" Version="1.1.0.1"
to your iOS project.
iOS: The '_GoogleUserMessagingPlatformAssemblyName' start tag on line 3 position 6 does not match the end tag of '_GoogleMobileAdsAssemblyName'. Line 3, position 132. /packages/xamarin.google.ios.usermessagingplatform/1.1.0.1/buildTransitive/Xamarin.Google.iOS.UserMessagingPlatform.targets
In your nuget cache, open the file "xamarin.google.ios.usermessagingplatform\1.1.0.1\buildTransitive\Xamarin.Google.iOS.UserMessagingPlatform.targets"
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
.Net 7: Error: Detected package downgrade: Xamarin.AndroidX.Lifecycle.LiveData from 2.6.2.3 to 2.5.1.1. Reference the package directly from the project to select a different version.
In your Android project, import the package:
Xamarin.AndroidX.Lifecycle.LiveData Version="2.6.2.3"
Error in ...\xamarin.androidx.lifecycle.common\2.6.2.3\buildTransitive\net7.0-android33.0\..\..\jar\androidx.lifecycle.lifecycle-common.jar:androidx/lifecycle/DispatchQueue.class: Type androidx.lifecycle.DispatchQueue is defined multiple times
In your Android project, import the packages:
"Xamarin.AndroidX.Activity" Version="1.8.2"
"Xamarin.AndroidX.Activity.Ktx" Version="1.8.2"
The consent form never appears
Turn on debug mode (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.
iOS: the consent form does not work on the simulator
Whether the consent form works on the iOS simulator depends on the Xcode version installed on your Mac. If it doesn't work, change your Xcode version or test on a real device. Testing on an iOS device connected directly to Windows is not supported: use a Mac.
iOS: the App Tracking Transparency dialog is never shown
Add 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.
Mac with Apple Silicon (M1, M2...): build error about the architecture
Add these lines to your csproj:
<PropertyGroup Condition="$(TargetFramework.Contains('-ios'))">
    <RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
    <ForceSimulatorX64ArchitectureInIDE>true</ForceSimulatorX64ArchitectureInIDE>
</PropertyGroup>
The package doesn't install or build on .NET 9
Version 2.5.0 and later target .NET 10. If your app is still on .NET 9, use version 2.4.0 of the plugin.
Ads load in the free version, but only one at a time
This is expected. The free version keeps one interstitial, one rewarded and one rewarded interstitial loaded at a time, and loads the next one only after the previous one has been shown. The licensed version can keep several ads ready.
The key is not valid for the current plugin version
A 1 Major Version licence is valid for the major version it was bought for: a key bought for 2.x does not unlock the licensed features on 3.x, and the plugin then runs as the free version. Buy a 3.x key, or keep using 2.x. Perpetual licences are valid for all versions.
Android build error: minSdkVersion cannot be smaller than version 24 (version 3.x)
Version 3.x runs Android on Google's Mobile Ads Next-Gen SDK, which requires Android 7.0 (API 24). Set SupportedOSPlatformVersion to 24.0 for Android in your csproj, or stay on version 2.x if your app must support older Android versions.
Native ad validator: "Advertiser assets outside native ad view" or "MediaView is too small for video"
Update to 3.0 or later: on Android the native ad view now resizes to its template. Then give the MTMediaView in your template at least 120x120, and avoid * rows around it in a Grid (use Auto), so the layout cannot shrink it.