要在 Capacitor 应用程序中处理 AdMob GDPR 许可,请使用 Google 的用户消息平台 (UMP) SDK: 在每次启动时请求许可信息,显示许可表单,如果需要,则只初始化广告 canRequestAds 当 is 为 true 时。您还需要一个隐私选项入口点,以便用户可以更改他们的选择,并且在 iOS 上,UMP 流程应在 App Tracking Transparency 提示之前。这个指南展示了两个设置:一个将 UMP 包装在 JavaScript 中的插件,以及一个小型本机许可桥 @capgo/capacitor-admob 使用一个小型本地同意桥梁。
什么是 UMP,为什么 AdMob 需要它
自 2024 年 1 月起,Google 需要发布广告的出版商在欧洲经济区(EEA)、英国和瑞士使用 Google 批准的同意管理平台(CMP)和 IAB 透明度和同意框架(TCF)集成的 CMP。UMP SDK 是 Google 自己的批准 CMP,免费且内置于 AdMob 控制台中。
UMP 为您做三件事:
- 检测用户是否位于需要同意的地区。
- 在用户的语言中显示您在 AdMob 中配置的同意消息。
- 将用户的选择存储为设备上的 TC 字符串(标准
IABTCF_keys 在UserDefaultsiOS 上和SharedPreferencesAndroid 上),Google Mobile Ads SDK 和中介合作伙伴在请求广告时读取。
您还可以使用 UMP 来处理美国州的隐私法规,AdMob 称之为“美国州”消息,从同一个控制台中。
步骤 1:在 AdMob 中创建消息
context 隐私 & 通知:
- 创建一个 欧洲法规 消息 (GDPR)。选择它适用的应用程序、语言和广告合作伙伴列表。
- 可选创建一个 美国州 消息。
- 在 iOS 上,创建一个 IDFA 解释 消息,如果您计划请求 App Tracking Transparency。UMP 会在系统 ATT 提示之前显示它。
- 发布消息。未发布的消息永远不会显示。
如果表单在您的应用程序中永远不会出现,未发布或未分配的消息是首先要检查的。
第 2 步:确定操作顺序
大多数应用程序都适用的顺序是:
- 应用程序启动
- 每次启动时使用 UMP 请求同意信息更新
- 如果需要,请加载并显示同意表单
- 如果
canRequestAds是 true,则启动移动广告 SDK 并加载广告 - 在 UMP 表示隐私选项需要时,在设置屏幕上显示一个“隐私设置”条目
Google 还允许在同意请求时并行启动移动广告 SDK,当 canRequestAds 从前一次会话中已经为 true 时,这可以缩短返回用户的首次广告时间
选项 A:将 UMP 包装在 JavaScript 中的插件
@capacitor-community/admob Capacitor requestConsentInfo, showConsentForm, showPrivacyOptionsForm 和 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();
}
本插件是上述顺序中的唯一例外:其README要求 AdMob.initialize() 在 requestConsentInfo(),并且在iOS上,直到 initialize() 已经运行。 initialize() 启动SDK,但不请求广告,因此关注的规则是直到 canRequestAds SDK AdMob.start().
Check canRequestAds 为真之前加载广告。如果您需要__CAPGO_KEEP_0__保持停止状态直到用户回答,请使用选项B,whereconsent在
选项 B:@capgo/capacitor-admob 使用原生 UMP 桥
@capgo/capacitor-admob 检查 AdMob.start()在每次广告加载之前,仅在启动时不够,因为用户可以从隐私选项表单中撤回同意。 AdMob.start().
bun add @capgo/capacitor-admob
bunx cap sync
添加 UMP SDK
- iOS: 添加 Google 的
GoogleUserMessagingPlatform使用 Swift Package Manager (Capacitor 默认情况下有 8 个项目使用 SPM) 或 CocoaPods 中相同名称的 pod,如果您的项目仍然使用 CocoaPods。 - Android: 在
android/app/build.gradle:
dependencies {
implementation "com.google.android.ump:user-messaging-platform:<latest version>"
}
使用 Google Maven 仓库的最新版本。
iOS 许可插件
创建 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,
]
}
}
从一个 CAPBridgeViewController 子类中注册它,并在视图控制器上设置该类 Main.storyboard:
import Capacitor
class MainViewController: CAPBridgeViewController {
override open func capacitorDidLoad() {
bridge?.registerPluginInstance(ConsentPlugin())
}
}
Android 许可插件
创建 android/app/src/main/java/com/example/app/ConsentPlugin.java (使用您的包名):
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;
}
}
在 MainActivity.java 之前 super.onCreate:
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
registerPlugin(ConsentPlugin.class);
super.onCreate(savedInstanceState);
}
}
context: 在Capgo UI中创建一个问题并讨论在开发新功能之前
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();
原生code变化需要重新构建应用商店。之后,用户同意复制到设置屏幕,广告插入逻辑可以与应用一起发布。 Capgo 实时更新.
原生__CAPGO_KEEP_0__更改需要一个新的商店构建。之后,Consent复制到您的设置屏幕和广告放置逻辑可以通过
context: Capgo营销网站。角色:按钮或链接标签。见:页面agents/[slug].astro。保留Capgo产品/品牌和开发者术语的原始形式。
const { showPrivacyEntry } = await startAds();
document.querySelector('#privacy-row')?.toggleAttribute('hidden', !showPrivacyEntry);
document.querySelector('#privacy-row')?.addEventListener('click', () => openPrivacyOptions());
步骤3:添加隐私选项入口点 canRequestAds context: 关于Capgo页面。角色:UI标签。见:页面about.astro。
步骤 4:iOS App 跟踪透明度
ATT与GDPR是不同的。ATT控制iOS设备上的IDFA访问权限。GDPR同意决定个人数据是否可以处理以及如何处理。通常,需要在iOS设备上为欧盟用户获取两者。
- 添加
NSUserTrackingUsageDescription到Info.plist. - 让UMP显示IDFA解释消息并触发ATT,如果您在AdMob中配置了该消息。否则,在同意流程之后请求ATT。
With @capgo/capacitor-admob __CAPGO_KEEP_0__也具有独立的
import { AdMob, TrackingAuthorizationStatus } from '@capgo/capacitor-admob';
const { status } = await AdMob.trackingAuthorizationStatus();
if (status === TrackingAuthorizationStatus.notDetermined) {
await AdMob.requestTrackingAuthorization();
}
Capgo 也有一个独立的 如果您需要ATT其他SDK,请不要忘记在隐私清单和App Store隐私标签中声明跟踪,见我们的 隐私清单指南 第5步:使用调试地理位置进行测试.
UMP仅在EEA、英国或瑞士用户面前显示GDPR表单。要测试其他地区:
UMP仅在EEA、英国或瑞士用户面前显示GDPR表单。要测试其他地区,请使用调试地理位置
- 运行应用程序一次,找到UMP中与您的设备的哈希ID相关的日志行。
- 将其添加为测试设备,并将调试地理设置为EEA。使用社区插件,传递
debugGeography并testDeviceIdentifiers到requestConsentInfo. 在本机桥中,设置DebugSettings在iOS上和ConsentDebugSettings在Android上(仅限调试构建)。 - 在运行之间重置同意(
resetConsentInfo()在社区插件中ConsentInformation.reset()本机)以便表单再次显示。
不要在发布构建中发送调试设置。将它们门控在一个构建标志上。
第6步:在应用程序中处理错误而不阻塞应用程序
请求许可可能会失败:无网络、配置错误的应用 ID 或未发布的消息。将失败视为“暂时不显示广告”,而不是崩溃:
- 如果
requestConsentInfoUpdate失败但canRequestAds之前会显示的,则仍可以请求广告。 - 如果表单无法加载,请记录日志并在下次启动时重试。
- 在显示许可表单时,不要显示广告。
故障排除
上下文:支持/高级支持页面或底部支持部分。角色:部分或页面标题。见于:页面 support-policy.astro。消息键 `support_policy_troubleshooting_title` (支持政策故障排除标题)。 表单永远不会显示。 Info.plist and AndroidManifest.xml 和
上下文:Capgo UI 中的 HTML 文本片段(父键 `compare_step_capacitor_text`)。页面/区域:Capgo 市场网站。角色:网站副本句子。见于:页面 alternatives/expo.astro,页面 alternatives/median.astro。消息键 `compare_step_capacitor_text` (Capacitor 比较步骤文本)。| 页面/区域:Capgo 市场网站。角色:短 UI 标签或导航项。见于:页面 trust.astro。消息键 `and` (和)。 用户关闭了它,没有选择,或者你在生产环境中调用了重置 code。
canRequestAds 对于美国用户,值为 false。 您发布了一个美国州的消息,用户选择了不出售或共享。通过 SDK 仅服务有限广告,不要绕过它。
媒体合作伙伴没有获得同意。 确保每个合作伙伴适配器都是最新的,并且在您的 GDPR 消息的广告合作伙伴列表中列出。
iOS ATT 提示永远不会出现。 NSUserTrackingUsageDescription 缺少了“允许应用程序请求跟踪”设置中的选项,或者用户在设置中禁用了“允许应用程序请求跟踪”。
形式之外的合规性
UMP 处理广告侧。您的应用程序仍然需要一个隐私政策,列出 AdMob、数据保留规则和处理数据请求的方式。我们的 GDPR 合规性清单 涵盖了应用程序的其余部分。