Per gestire il consenso GDPR di AdMob in un'app Capacitor, usa l'UMP (User Messaging Platform) di Google SDK: richiedi le informazioni sul consenso a ogni avvio, mostra il modulo se necessario e inizializza gli annunci solo quando canRequestAds è true. Ti serve anche un punto di accesso alle opzioni privacy per permettere agli utenti di cambiare la propria scelta e, su iOS, il flusso UMP deve precedere il prompt App Tracking Transparency. Questa guida mostra il flusso completo con due setup: un plugin che incapsula UMP in JavaScript, e @capgo/capacitor-admob con un piccolo bridge nativo per il consenso.
Cosa fa UMP e perché AdMob lo richiede
Dal gennaio 2024, Google richiede ai publisher che servono annunci a utenti in SEE, Regno Unito e Svizzera di utilizzare una piattaforma di gestione del consenso (CMP) certificata da Google e integrata con l'IAB Transparency and Consent Framework (TCF). L'UMP SDK è la CMP certificata di Google, gratuita e integrata nella console AdMob.
UMP fa tre cose per te:
- Rileva se l'utente si trova in una regione in cui il consenso è obbligatorio.
- Mostra il messaggio di consenso configurato in AdMob, nella lingua dell'utente.
- Salva le scelte dell'utente come stringa TC sul dispositivo (lo standard
IABTCF_chiavi inUserDefaultssu iOS eSharedPreferencessu Android), che Google Mobile Ads SDK e i partner di mediazione leggono quando richiedono annunci.
Puoi anche usare UMP per le normative sulla privacy degli stati USA, che AdMob chiama messaggi “US states”, dalla stessa console.
Passo 1: crea i messaggi in AdMob
Nella console AdMob, vai a Privacy e messaggistica:
- Crea un Normative europee messaggio (GDPR). Scegli a quali app si applica, le lingue e la tua lista di partner pubblicitari.
- Facoltativamente, crea un Stati USA messaggio.
- Su iOS, crea un messaggio esplicativo sull'IDFA se prevedi di richiedere App Tracking Transparency. UMP lo mostrerà prima del prompt ATT di sistema.
- Pubblica i messaggi. Un messaggio non pubblicato non verrà mai mostrato.
Se il form non appare più nell'app, controlla prima se il messaggio è non pubblicato o non assegnato.
Passo 2: decidi l'ordine delle operazioni
The order that works for most apps:
- L'app si avvia.
- Request consent info update with UMP (every launch).
- Se richiesto, carica e mostra il form di consenso.
- Se
canRequestAdsè true, avvia il Mobile Ads SDK e carica gli annunci. - Mostra una voce "Impostazioni privacy" nella schermata delle impostazioni quando UMP indica che le opzioni privacy sono obbligatorie.
Google consente anche di avviare il Mobile Ads SDK in parallelo alla richiesta di consenso quando canRequestAds era già true da una sessione precedente. Questo riduce il tempo fino al primo annuncio per gli utenti ricorrenti.
Opzione A: un plugin che incapsula UMP in JavaScript
@capacitor-community/admob (la versione 8 supporta Capacitor 8) espone direttamente i metodi UMP: requestConsentInfo, showConsentForm, showPrivacyOptionsForm e resetConsentInfo.
bun add @capacitor-community/admob
bunx cap sync
import {
AdMob,
AdmobConsentStatus,
AdmobConsentDebugGeography,
} from '@capacitor-community/admob';
export async function initAdsWithConsent(isDebug: boolean) {
await AdMob.initialize();
let info = await AdMob.requestConsentInfo(
isDebug
? {
debugGeography: AdmobConsentDebugGeography.EEA,
testDeviceIdentifiers: ['YOUR_TEST_DEVICE_ID'],
}
: undefined,
);
if (info.isConsentFormAvailable && info.status === AdmobConsentStatus.REQUIRED) {
info = await AdMob.showConsentForm();
}
// The enum value is the string 'REQUIRED'
const showPrivacyEntry = String(info.privacyOptionsRequirementStatus) === 'REQUIRED';
return { canRequestAds: info.canRequestAds, showPrivacyEntry };
}
export async function openPrivacyOptions() {
await AdMob.showPrivacyOptionsForm();
}
Questo plugin è l'unica eccezione all'ordine sopra indicato: il suo README richiede AdMob.initialize() prima requestConsentInfo(), e su iOS il modulo di consenso non può essere mostrato finché initialize() has run. initialize() avvia SDK ma non richiede annunci, quindi la regola che conta è non caricare mai un annuncio finché canRequestAds non è vero. Se hai bisogno che SDK resti fermo finché l'utente non ha risposto, usa l'Opzione B, dove il consenso viene eseguito prima che AdMob.start().
Verifica canRequestAds prima di ogni caricamento di annunci, non solo all'avvio, perché l'utente può revocare il consenso dal modulo delle opzioni sulla privacy.
Opzione B: @capgo/capacitor-admob con un bridge UMP nativo
@capgo/capacitor-admob ti offre AdMob.start(), la configurazione della richiesta, banner, interstitial, rewarded e rewarded interstitial, oltre all'autorizzazione al tracciamento su iOS. Non fa da wrapper a UMP, quindi devi aggiungere un piccolo plugin locale all'app che esegue il flusso di consenso prima di chiamare AdMob.start().
bun add @capgo/capacitor-admob
bunx cap sync
Aggiungi il SDK UMP
- iOS: aggiungi il
GoogleUserMessagingPlatformpackage tramite Swift Package Manager (Capacitor 8 usa SPM di default nei progetti), o il pod CocoaPods con lo stesso nome se il tuo progetto usa ancora CocoaPods. - Android: nella
android/app/build.gradle:
dependencies {
implementation "com.google.android.ump:user-messaging-platform:<latest version>"
}
Usa l'ultima release dal repository Maven di Google.
plugin per il consenso iOS
Crea ios/App/App/ConsentPlugin.swift:
import Capacitor
import UserMessagingPlatform
@objc(ConsentPlugin)
public class ConsentPlugin: CAPPlugin, CAPBridgedPlugin {
public let identifier = "ConsentPlugin"
public let jsName = "Consent"
public let pluginMethods: [CAPPluginMethod] = [
CAPPluginMethod(name: "gather", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "showPrivacyOptions", returnType: CAPPluginReturnPromise),
]
@objc func gather(_ call: CAPPluginCall) {
let parameters = RequestParameters()
DispatchQueue.main.async {
ConsentInformation.shared.requestConsentInfoUpdate(with: parameters) { error in
if let error = error {
call.reject(error.localizedDescription)
return
}
guard let vc = self.bridge?.viewController else {
call.reject("No view controller")
return
}
ConsentForm.loadAndPresentIfRequired(from: vc) { formError in
if let formError = formError {
call.reject(formError.localizedDescription)
return
}
call.resolve(self.state())
}
}
}
}
@objc func showPrivacyOptions(_ call: CAPPluginCall) {
DispatchQueue.main.async {
guard let vc = self.bridge?.viewController else {
call.reject("No view controller")
return
}
ConsentForm.presentPrivacyOptionsForm(from: vc) { formError in
if let formError = formError {
call.reject(formError.localizedDescription)
return
}
call.resolve(self.state())
}
}
}
private func state() -> [String: Any] {
return [
"canRequestAds": ConsentInformation.shared.canRequestAds,
"privacyOptionsRequired":
ConsentInformation.shared.privacyOptionsRequirementStatus == .required,
]
}
}
Registralo da una CAPBridgeViewController subclass e imposta quella classe sul view controller in Main.storyboard:
import Capacitor
class MainViewController: CAPBridgeViewController {
override open func capacitorDidLoad() {
bridge?.registerPluginInstance(ConsentPlugin())
}
}
Plugin di consenso Android
Crea android/app/src/main/java/com/example/app/ConsentPlugin.java (usa il nome del tuo pacchetto):
package com.example.app;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import com.google.android.ump.ConsentInformation;
import com.google.android.ump.ConsentRequestParameters;
import com.google.android.ump.UserMessagingPlatform;
@CapacitorPlugin(name = "Consent")
public class ConsentPlugin extends Plugin {
@PluginMethod
public void gather(PluginCall call) {
ConsentInformation info = UserMessagingPlatform.getConsentInformation(getContext());
ConsentRequestParameters params = new ConsentRequestParameters.Builder().build();
getActivity().runOnUiThread(() ->
info.requestConsentInfoUpdate(
getActivity(),
params,
() -> UserMessagingPlatform.loadAndShowConsentFormIfRequired(getActivity(), formError -> {
if (formError != null) {
call.reject(formError.getMessage());
return;
}
call.resolve(state(info));
}),
requestError -> call.reject(requestError.getMessage())
)
);
}
@PluginMethod
public void showPrivacyOptions(PluginCall call) {
getActivity().runOnUiThread(() ->
UserMessagingPlatform.showPrivacyOptionsForm(getActivity(), formError -> {
if (formError != null) {
call.reject(formError.getMessage());
return;
}
call.resolve(state(UserMessagingPlatform.getConsentInformation(getContext())));
})
);
}
private JSObject state(ConsentInformation info) {
JSObject ret = new JSObject();
ret.put("canRequestAds", info.canRequestAds());
ret.put(
"privacyOptionsRequired",
info.getPrivacyOptionsRequirementStatus() ==
ConsentInformation.PrivacyOptionsRequirementStatus.REQUIRED
);
return ret;
}
}
Registralo in MainActivity.java prima super.onCreate:
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
registerPlugin(ConsentPlugin.class);
super.onCreate(savedInstanceState);
}
}
Chiamalo da TypeScript
import { registerPlugin } from '@capacitor/core';
import { AdMob, BannerAd } from '@capgo/capacitor-admob';
interface ConsentState {
canRequestAds: boolean;
privacyOptionsRequired: boolean;
}
interface ConsentPlugin {
gather(): Promise<ConsentState>;
showPrivacyOptions(): Promise<ConsentState>;
}
const Consent = registerPlugin<ConsentPlugin>('Consent');
export async function startAds() {
let state: ConsentState;
try {
state = await Consent.gather();
} catch (err) {
console.warn('Consent flow failed', err);
return { showPrivacyEntry: false };
}
if (state.canRequestAds) {
await AdMob.start();
const banner = new BannerAd({
adUnitId: 'ca-app-pub-3940256099942544/2934735716', // Google test banner (iOS)
position: 'bottom',
});
await banner.show();
}
return { showPrivacyEntry: state.privacyOptionsRequired };
}
export const openPrivacyOptions = () => Consent.showPrivacyOptions();
Le modifiche native code richiedono un nuovo build per lo store. Dopo, il testo del consenso nella schermata impostazioni e la logica di posizionamento degli annunci possono essere rilasciati con Live updates di Capgo.
Passo 3: aggiungi il punto di accesso alle opzioni privacy
Quando UMP segnala che le opzioni privacy sono richieste, Google si aspetta un modo visibile per riaprire le scelte di consenso. Aggiungi una riga “Impostazioni privacy” alla tua pagina delle impostazioni e nascondila quando non è richiesta:
const { showPrivacyEntry } = await startAds();
document.querySelector('#privacy-row')?.toggleAttribute('hidden', !showPrivacyEntry);
document.querySelector('#privacy-row')?.addEventListener('click', () => openPrivacyOptions());
Dopo che l'utente ha modificato le sue scelte, leggi canRequestAds again before the next ad request.
Passaggio 4: App Tracking Transparency su iOS
L'ATT è separata dal GDPR. L'ATT controlla l'accesso all'IDFA su iOS, mentre il consenso GDPR stabilisce se e come i dati personali possono essere trattati. Di solito servono entrambi per un utente UE su iOS.
- Aggiungi
NSUserTrackingUsageDescriptionaInfo.plist. - Lascia che UMP mostri il messaggio esplicativo dell'IDFA e attivi l'ATT, se hai configurato quel messaggio in AdMob. Altrimenti, richiedi tu l'ATT dopo il flusso di consenso.
Con @capgo/capacitor-admob puoi leggere e richiedere lo stato dell'ATT direttamente:
import { AdMob, TrackingAuthorizationStatus } from '@capgo/capacitor-admob';
const { status } = await AdMob.trackingAuthorizationStatus();
if (status === TrackingAuthorizationStatus.notDetermined) {
await AdMob.requestTrackingAuthorization();
}
Capgo ha anche una versione standalone plugin App Tracking Transparency se ti serve l'ATT per altri SDK. Non dimenticare di dichiarare il tracking nel tuo privacy manifest e nelle etichette privacy dell'App Store, vedi la nostra guida al privacy manifest.
Passaggio 5: testa con debug geography
UMP mostra il modulo GDPR solo agli utenti localizzati nello SEE, nel Regno Unito o in Svizzera. Per testare in altre aree:
- Esegui l'app una volta e individua la riga di log di UMP con l'ID hash del tuo dispositivo.
- Aggiungilo come dispositivo di test e imposta la geografia di debug su EEA. Con il plugin della community, passa
debugGeographyetestDeviceIdentifiersarequestConsentInfo. In un bridge nativo, impostaDebugSettingssu iOS eConsentDebugSettingssu Android solo nelle build di debug. - Reimposta il consenso tra le esecuzioni (
resetConsentInfo()nel plugin della community,ConsentInformation.reset()in modo nativo) così il modulo ricompare.
Non rilasciare mai impostazioni di debug nelle build di release. Falle dipendere da una build flag.
Passo 6: gestire gli errori senza bloccare l'app
Le richieste di consenso possono fallire: nessuna rete, app ID configurato male o nessun messaggio pubblicato. Tratta il fallimento come “niente ads per ora”, non come un crash:
- Se
requestConsentInfoUpdatefallisce macanRequestAdsera true in una sessione precedente, puoi comunque richiedere annunci. - Se il modulo non si carica, registra l'errore e riprova al prossimo avvio.
- Non mostrare mai annunci mentre il modulo di consenso è a schermo.
Risoluzione problemi
Il modulo non viene mai visualizzato. Messaggio non pubblicato, non assegnato a questo app ID, oppure ti trovi fuori dall'EEA senza debug geography. Controlla l'app ID di AdMob in Info.plist e AndroidManifest.xml corrisponde alla console.
Il modulo appare a ogni avvio. L'utente lo ha chiuso senza scegliere, o hai chiamato reset in produzione code.
canRequestAds false per gli utenti USA. Hai pubblicato un messaggio per gli stati USA e l'utente ha negato la vendita o la condivisione. Mostra solo annunci limitati tramite SDK, non bypassarlo.
I partner di mediazione non ricevono il consenso. Assicurati che ogni adapter del partner sia aggiornato e presente nella lista dei partner pubblicitari del tuo messaggio GDPR.
Il prompt iOS ATT non appare mai. NSUserTrackingUsageDescription manca, o l'utente ha disattivato "Consenti alle app di richiedere il tracciamento" nelle Impostazioni.
Compliance oltre il modulo
UMP gestisce la parte pubblicitaria. La tua app ha comunque bisogno di una privacy policy che elenchi AdMob, le regole di conservazione dei dati e un modo per gestire le richieste sui dati. La nostra checklist per la conformità GDPR copre il resto dell'app.