Salta al contenuto principale

Come gestire il consenso GDPR di AdMob in Capacitor

Gestisci il consenso GDPR di AdMob in un'app Capacitor con l'UMP di Google SDK: modulo di consenso, opzioni privacy, ordine ATT, debug della posizione e gestione errori, con code.

Come gestire il consenso GDPR di AdMob in Capacitor

Per gestire il consenso GDPR di AdMob in un'app Capacitor, usa l'User Messaging Platform (UMP) di Google SDK: richiedi le informazioni sul consenso ad ogni avvio, mostra il modulo di consenso 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 scelta e, su iOS, il flusso UMP deve precedere il prompt di App Tracking Transparency. Questa guida mostra l'intero flusso con due setup: un plugin che incapsula UMP in JavaScript, e @capgo/capacitor-admob con un piccolo bridge nativo per il consenso.

Cosa fa l'UMP e perché AdMob lo richiede

Da gennaio 2024, Google richiede agli editori che mostrano annunci agli utenti nello SEE, nel Regno Unito e in 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.

L'UMP fa tre cose per te:

  1. Rileva se l'utente si trova in una regione in cui è richiesto il consenso.
  2. Mostra il messaggio di consenso configurato in AdMob, nella lingua dell'utente.
  3. Salva le scelte dell'utente come TC string sul dispositivo (lo standard IABTCF_ chiavi in UserDefaults su iOS e SharedPreferences su Android), che il Google Mobile Ads SDK e i partner di mediazione leggono quando richiedono annunci.

Puoi usare l'UMP anche per le normative sulla privacy degli stati USA, che AdMob chiama messaggi "US states", sempre dalla stessa console.

Passaggio 1: crea i messaggi in AdMob

Nella console di AdMob, vai su Privacy e messaggistica:

  • Crea un Regolamenti europei messaggio (GDPR). Scegli a quali app applicarlo, le lingue e l'elenco dei partner pubblicitari.
  • Opzionalmente, crea un Stati USA messaggio.
  • Su iOS, crea un messaggio esplicativo IDFA se intendi richiedere l'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 modulo non appare più nella tua app, la prima cosa da controllare è se il messaggio è non pubblicato o non assegnato.

Passaggio 2: decidi l'ordine delle operazioni

L'ordine ideale per la maggior parte delle app:

  1. L'app si avvia.
  2. Richiedi l'aggiornamento delle info sul consenso con UMP (a ogni avvio).
  3. Se necessario, carica e mostra il modulo di consenso.
  4. Se canRequestAds è true, avvia il Mobile Ads SDK e carica gli annunci.
  5. Mostra una voce “Impostazioni privacy” nella schermata settings quando UMP indica che le opzioni privacy sono richieste.

Google permette anche di avviare il Mobile Ads SDK in parallelo alla richiesta di consenso quando canRequestAds era già true in una sessione precedente. Questo riduce i tempi per il 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 i metodi UMP direttamente: 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 fondamentale è non caricare mai un annuncio finché canRequestAds è vera. Se hai bisogno che SDK rimanga fermo finché l'utente non ha risposto, usa l'Opzione B, dove il consenso viene gestito prima AdMob.start().

Controlla canRequestAds di ogni caricamento di annunci, non solo all'avvio, poiché 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 include UMP, quindi devi aggiungere un piccolo plugin locale per gestire il flusso di consenso prima di chiamare AdMob.start().

bun add @capgo/capacitor-admob
bunx cap sync

Aggiungi il SDK UMP

  • iOS: aggiungi il GoogleUserMessagingPlatform package tramite Swift Package Manager (Capacitor 8 usa SPM di default), o il pod CocoaPods con lo stesso nome se il tuo progetto usa ancora CocoaPods.
  • Android: in android/app/build.gradle:
dependencies {
    implementation "com.google.android.ump:user-messaging-platform:<latest version>"
}

Usa l'ultima release dal repository Maven di Google.

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())
    }
}

Crea android/app/src/main/java/com/example/app/ConsentPlugin.java (usa il tuo package name):

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();

Native code changes need a new store build. After that, consent copy in your settings screen and ad placement logic can be shipped with live updates di Capgo.

Passaggio 3: aggiungi l'entry point per le opzioni privacy

Quando UMP segnala che sono necessarie le opzioni privacy, Google richiede 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 scelte, leggi canRequestAds di nuovo prima della prossima richiesta di ad.

Passaggio 4: App Tracking Transparency su iOS

L'ATT è distinta dal GDPR. L'ATT controlla l'accesso all'IDFA su iOS, mentre il consenso GDPR determina se e come i dati personali possono essere trattati. Solitamente, per un utente UE su iOS, servono entrambi.

  • Aggiungi NSUserTrackingUsageDescription a Info.plist.
  • Lascia che UMP mostri il messaggio esplicativo dell'IDFA e attivi l'ATT, se hai configurato tale messaggio in AdMob. Altrimenti, richiedi l'ATT manualmente dopo il flusso di consenso.

Con @capgo/capacitor-admob puoi leggere e richiedere lo stato 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. Ricorda di dichiarare il tracking nel tuo privacy manifest e nelle etichette sulla privacy dell'App Store; consulta la nostra guida al privacy manifest.

Passaggio 5: testa con debug geography

UMP mostra il modulo GDPR solo agli utenti situati in SEE, Regno Unito o Svizzera. Per testare altrove:

  • Avvia l'app e cerca nel log la riga UMP con l'ID hash del tuo dispositivo.
  • Aggiungilo come dispositivo di test e imposta la geografia di debug su SEE. Con il plugin della community, passa debugGeography e testDeviceIdentifiers a requestConsentInfo. In un bridge nativo, imposta DebugSettings su iOS e ConsentDebugSettings su Android solo nelle build di debug.
  • Resetta il consenso tra un'esecuzione e l'altra (resetConsentInfo() nel plugin della community, ConsentInformation.reset() nativamente) per far riapparire il modulo.

Non includere mai impostazioni di debug nelle build di release. Gestiscile tramite una build flag.

Passaggio 6: gestisci gli errori senza bloccare l'app

Le richieste di consenso possono fallire: assenza di rete, App ID errato o messaggio non pubblicato. Gestisci l'errore come "nessuna pubblicità per ora", non come un crash:

  • Se requestConsentInfoUpdate fallisce ma canRequestAds era vero in una sessione precedente, puoi comunque richiedere gli annunci.
  • Se il modulo non si carica, logga 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 mostrato. Messaggio non pubblicato, non assegnato a questo app ID o ti trovi fuori dallo SEE senza debug geography. Controlla l'app ID di AdMob in Info.plist e AndroidManifest.xml corrisponde alla console.

Il modulo appare ad ogni avvio. L'utente lo ha chiuso senza scegliere, oppure chiami 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 necessita comunque di una privacy policy che elenchi AdMob, le regole di retention dei dati e un modo per gestire le richieste sui dati. La nostra checklist per la compliance GDPR copre tutto il resto dell'app.

Live update per le app Capacitor

Quando un bug del web layer è live, rilascia il fix tramite Capgo invece di aspettare giorni per l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale percorso di review.

Le ultime dal nostro blog

Capgo ti offre i migliori insight per creare un'app mobile davvero professionale.