Para gestionar el consentimiento GDPR de AdMob en una app de Capacitor, utiliza la User Messaging Platform (UMP) de Google (SDK): solicita la información de consentimiento en cada inicio, muestra el formulario de consentimiento si es necesario y solo inicializa los anuncios cuando canRequestAds sea true. También necesitas un punto de entrada para las opciones de privacidad para que los usuarios puedan cambiar su elección, y en iOS el flujo de UMP debe preceder al prompt de App Tracking Transparency. Esta guía muestra el flujo completo con dos configuraciones: un plugin que envuelve UMP en JavaScript, y @capgo/capacitor-admob con un pequeño puente nativo de consentimiento.
Qué hace UMP y por qué AdMob lo requiere
Desde enero de 2024, Google exige a los editores que sirven anuncios a usuarios en el EEE, el Reino Unido y Suiza que utilicen una plataforma de gestión de consentimiento (CMP) certificada por Google e integrada con el IAB Transparency and Consent Framework (TCF). El UMP SDK es la CMP certificada de Google, gratuita e integrada en la consola de AdMob.
UMP hace tres cosas por ti:
- Detecta si el usuario está en una región donde se requiere consentimiento.
- Muestra el mensaje de consentimiento que configuraste en AdMob, en el idioma del usuario.
- Guarda las elecciones del usuario como una cadena TC en el dispositivo (el estándar
IABTCF_keys inUserDefaultsen iOS ySharedPreferencesen Android), que el Google Mobile Ads SDK y los socios de mediación leen al solicitar anuncios.
También puedes usar UMP para las regulaciones de privacidad de los estados de EE. UU., que AdMob denomina mensajes de “US states”, desde la misma consola.
Paso 1: crear los mensajes en AdMob
En la consola de AdMob, ve a Privacidad y mensajería:
- Crea un Regulaciones europeas mensaje (GDPR). Elige a qué apps se aplica, los idiomas y tu lista de socios de publicidad.
- Crea opcionalmente un US states mensaje.
- En iOS, crea un explicador de IDFA mensaje si planeas solicitar App Tracking Transparency. UMP lo mostrará antes del prompt del sistema ATT.
- Publica los mensajes. Un mensaje sin publicar nunca se mostrará.
Si más adelante el formulario no aparece en tu app, lo primero que debes revisar es si el mensaje está sin publicar o sin asignar.
Paso 2: decidir el orden de las operaciones
El orden que funciona para la mayoría de las apps:
- La app se inicia.
- Request consent info update with UMP (every launch).
- Si es necesario, cargar y mostrar el formulario de consentimiento.
- Si
canRequestAdses true, iniciar el Mobile Ads SDK y cargar anuncios. - Muestra una opción de “Configuración de privacidad” en tu pantalla de ajustes cuando UMP indique que se requieren opciones de privacidad.
Google también permite iniciar el Mobile Ads SDK en paralelo con la solicitud de consentimiento cuando canRequestAds ya era true desde una sesión anterior. Eso reduce el tiempo hasta el primer anuncio para usuarios recurrentes.
Opción A: un plugin que encapsula UMP en JavaScript
@capacitor-community/admob (la versión 8 soporta Capacitor 8) expone los métodos de UMP directamente: requestConsentInfo, showConsentForm, showPrivacyOptionsForm y 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();
}
Este plugin es la única excepción al orden anterior: su README requiere AdMob.initialize() antes requestConsentInfo(), y en iOS el formulario de consentimiento no puede mostrarse hasta que initialize() se ha ejecutado. initialize() inicia el SDK pero no solicita anuncios, así que la regla que importa es no cargar nunca un anuncio hasta que canRequestAds sea true. Si necesitas que el SDK permanezca detenido hasta que el usuario responda, usa la Opción B, donde el consentimiento se ejecuta antes de AdMob.start().
Verificar canRequestAds antes de cada carga de anuncio, no solo al inicio, ya que el usuario puede retirar el consentimiento desde el formulario de opciones de privacidad.
Opción B: @capgo/capacitor-admob con un bridge nativo de UMP
@capgo/capacitor-admob te ofrece AdMob.start(), configuración de solicitud, anuncios banner, intersticiales, recompensados e intersticiales recompensados, además de la autorización de seguimiento en iOS. No envuelve UMP, por lo que debes añadir un pequeño plugin local de la app que ejecute el flujo de consentimiento antes de llamar a AdMob.start().
bun add @capgo/capacitor-admob
bunx cap sync
Añadir el SDK de UMP
- iOS: añadir el de Google
GoogleUserMessagingPlatformpaquete de Google con Swift Package Manager (Capacitor 8 proyectos usan SPM por defecto), o el pod de CocoaPods con el mismo nombre si tu proyecto sigue usando CocoaPods. - Android: en
android/app/build.gradle:
dependencies {
implementation "com.google.android.ump:user-messaging-platform:<latest version>"
}
Usa la última versión del repositorio Maven de Google.
Plugin de consentimiento para iOS
Crear 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,
]
}
}
Regístralo desde una CAPBridgeViewController subclase, y asigna esa clase al view controller en Main.storyboard:
import Capacitor
class MainViewController: CAPBridgeViewController {
override open func capacitorDidLoad() {
bridge?.registerPluginInstance(ConsentPlugin())
}
}
Plugin de consentimiento para Android
Crear android/app/src/main/java/com/example/app/ConsentPlugin.java (usa tu nombre de paquete):
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;
}
}
Regístralo en MainActivity.java antes super.onCreate:
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
registerPlugin(ConsentPlugin.class);
super.onCreate(savedInstanceState);
}
}
Llamarlo desde 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();
Los cambios en code nativo necesitan una nueva build para la tienda. Después, el texto de consentimiento de tu pantalla de ajustes y la lógica de colocación de anuncios se pueden enviar con actualizaciones Live Update de Capgo.
Paso 3: añadir el punto de entrada de opciones de privacidad
Cuando UMP indica que se requieren opciones de privacidad, Google espera una forma visible de reabrir las opciones de consentimiento. Añade una fila «Ajustes de privacidad» a tu página de ajustes y ocúltala cuando no sea necesaria:
const { showPrivacyEntry } = await startAds();
document.querySelector('#privacy-row')?.toggleAttribute('hidden', !showPrivacyEntry);
document.querySelector('#privacy-row')?.addEventListener('click', () => openPrivacyOptions());
Después de que el usuario cambie sus opciones, lee canRequestAds again before the next ad request.
Paso 4: App Tracking Transparency en iOS
ATT es independiente del RGPD. ATT controla el acceso al IDFA en iOS. El consentimiento del RGPD controla si se pueden tratar datos personales y cómo. Normalmente necesitas ambos para un usuario de la UE en iOS.
- Añadir
NSUserTrackingUsageDescriptionaInfo.plist. - Deja que UMP muestre el mensaje explicativo de IDFA y active ATT si configuraste ese mensaje en AdMob. De lo contrario, solicita ATT tú mismo después del flujo de consentimiento.
Con @capgo/capacitor-admob puedes consultar el estado de ATT y solicitar ATT directamente:
import { AdMob, TrackingAuthorizationStatus } from '@capgo/capacitor-admob';
const { status } = await AdMob.trackingAuthorizationStatus();
if (status === TrackingAuthorizationStatus.notDetermined) {
await AdMob.requestTrackingAuthorization();
}
Capgo también tiene un plugin independiente de App Tracking Transparency si necesitas ATT para otros SDKs. No olvides declarar el rastreo en tu privacy manifest y en las etiquetas de privacidad de App Store, consulta nuestra guía de privacy manifest.
Paso 5: probar con geografía de debug
UMP solo muestra el formulario GDPR a los usuarios que ubica en el EEE, Reino Unido o Suiza. Para probar en otras ubicaciones:
- Ejecuta la app una vez y busca la línea de log de UMP con el ID hasheado de tu dispositivo.
- Añádela como dispositivo de prueba y configura la geografía de depuración en la EEE. Con el plugin de la comunidad, pasa
debugGeographyytestDeviceIdentifiersarequestConsentInfo. En un puente nativo, configuraDebugSettingsen iOS yConsentDebugSettingsen Android solo en builds de depuración. - Restablece el consentimiento entre ejecuciones (
resetConsentInfo()en el plugin de la comunidad,ConsentInformation.reset()natively) so the form shows again.
Nunca incluyas configuraciones de depuración en builds de release. Condiciónalas con una bandera de build.
Paso 6: gestionar errores sin bloquear la app
Las solicitudes de consentimiento pueden fallar: sin red, app ID mal configurado o sin mensaje publicado. Trata el fallo como «sin anuncios por ahora», no como un crash:
- Si
requestConsentInfoUpdatefalla perocanRequestAdsera true en una sesión anterior, aún puedes solicitar anuncios. - Si el formulario no carga, regístralo y reintenta en el próximo inicio.
- Never show ads while the consent form is on screen.
Solución de problemas
El formulario nunca se muestra. Mensaje no publicado, no asignado a este ID de app, o estás fuera del EEE sin geografía de depuración. Revisa el ID de app de AdMob en Info.plist y AndroidManifest.xml coincide con la consola.
El formulario se muestra en cada inicio. El usuario lo cerró sin elegir, o llamas a reset en producción code.
canRequestAds false para usuarios de EE. UU. Publicaste un mensaje de estados de EE. UU. y el usuario rechazó la venta o el uso compartido. Sirve solo anuncios limitados a través de SDK; no lo evites.
Los socios de mediación no reciben consentimiento. Asegúrate de que cada adaptador de partner esté actualizado y aparezca en la lista de socios publicitarios de tu mensaje de GDPR.
El prompt de iOS ATT nunca aparece. NSUserTrackingUsageDescription falta, o el usuario desactivó “Permitir que las apps soliciten rastreo” en Ajustes.
Cumplimiento más allá del formulario
UMP gestiona la parte de anuncios. Tu app aún necesita una política de privacidad que mencione AdMob, reglas de retención de datos y cómo gestionar solicitudes de datos. Nuestro Checklist de cumplimiento de GDPR cubre el resto de la app.