CapacitorアプリでAdMob GDPR同意を管理するには、GoogleのUser Messaging Platform (UMP) SDKを使用します。起動のたびに同意情報をリクエストし、必要に応じて同意フォームを表示し、 canRequestAds がtrueの場合にのみ広告を初期化します。また、ユーザーが選択を変更できるようにプライバシー設定への入り口を用意する必要があります。iOSでは、UMPのフローをApp Tracking Transparencyのプロンプトより前に実行する必要があります。このガイドでは、UMPをJavaScriptでラップするプラグインと、2つのセットアップを用いた完全なフローを紹介します。 @capgo/capacitor-admob 軽量なネイティブの同意ブリッジを使用して実現します。
UMPの機能とAdMobがそれを必要とする理由
2024年1月より、GoogleはEEA、英国、スイスのユーザーに広告を配信するパブリッシャーに対し、IAB Transparency and Consent Framework (TCF) と統合されたGoogle認定の同意管理プラットフォーム (CMP) の使用を義務付けています。UMP SDK はGoogle独自の認定CMPであり、AdMobコンソールに組み込まれた無料のツールです。
UMPは主に以下の3つの役割を担います:
- ユーザーが同意が必要な地域にいるかどうかを検出します。
- AdMobで設定した同意メッセージを、ユーザーの言語で表示します。
- ユーザーの選択をTC文字列としてデバイスに保存します(標準的な
IABTCF_キーを使用して、UserDefaultsiOSでは、SharedPreferences(Androidの場合)、Google Mobile Ads SDK およびメディエーションパートナーが広告リクエスト時に読み取るものです。
同じコンソールから、AdMobが「US states」メッセージと呼んでいる米国の州のプライバシー規制向けにUMPを使用することも可能です。
ステップ 1: AdMobでメッセージを作成する
AdMobコンソールで、 プライバシーとメッセージ:
- から、 欧州の規制に基づく メッセージ(GDPR)を作成します。適用するアプリ、言語、広告パートナーのリストを選択してください。
- 必要に応じて、 米国の州別の メッセージも作成できます。
- iOSでは、 IDFAの説明 メッセージを作成します(App Tracking Transparencyをリクエストする予定がある場合)。UMPは、システムのATTプロンプトが表示される前にこのメッセージを表示します。
- メッセージを公開してください。未公開のメッセージは表示されません。
アプリでフォームが全く表示されない場合は、まずメッセージが未公開または未割り当てになっていないか確認してください。
ステップ 2: 実行順序の決定
ほとんどのアプリで推奨される順序:
- アプリの起動
- UMPを使用して同意情報の更新をリクエスト(起動ごと)
- 必要な場合は、同意フォームをロードして表示
- もし
canRequestAdsが true の場合、Mobile Ads SDK を開始して広告をロード - UMP がプライバシー設定の選択が必要だと判断した際、設定画面に「プライバシー設定」のエントリを表示します。
また、Google では同意リクエストと並行して Mobile Ads SDK を開始することも可能です。これは、 canRequestAds が前回のセッションからすでに true だった場合に有効です。これにより、リピーターの初回広告表示までの時間が短縮されます。
オプション A: UMP を JavaScript でラップするプラグイン
@capacitor-community/admob (バージョン8はCapacitor 8をサポートしています) UMPメソッドを直接公開しています: 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を開始しますが広告のリクエストは行いません。そのため、重要なルールは「以下がtrueになるまで広告をロードしないこと」です: canRequestAds がtrueであること。ユーザーが回答するまでSDKを停止させておきたい場合は、オプションBを使用してください。オプションBでは、起動時だけでなく、 AdMob.start().
確認 canRequestAds すべての広告ロード前に同意プロセスを実行します。これは、ユーザーがプライバシー設定フォームから同意を撤回できるためです。
オプションB: ネイティブUMPブリッジを使用した@capgo/capacitor-admob
@capgo/capacitor-admob により、以下が可能になります: AdMob.start(), リクエスト設定、バナー、インタースティシャル、リワード広告、リワードインタースティシャル広告、およびiOSのトラッキング許可を扱います。UMPをラップしていないため、広告を呼び出す前に同意フローを実行する小さなアプリローカルプラグインを追加します。 AdMob.start().
bun add @capgo/capacitor-admob
bunx cap sync
UMP SDK を追加
- iOS: Googleの
GoogleUserMessagingPlatformパッケージをSwift Package Managerで追加するか (Capacitor 8のプロジェクトはデフォルトでSPMを使用)、プロジェクトがまだCocoaPodsを使用している場合は同名のCocoaPods podを追加してください。 - 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 サブクラスで、そのクラスを view controller に設定します 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);
}
}
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 の変更には新しいストアビルドが必要です。その後は、設定画面の同意文言や広告配置ロジックも一緒に配信できます: Capgo Live Updates.
ステップ 3: プライバシーオプションのエントリポイントを追加する
UMPがプライバシーオプションの表示が必要だと示した場合、Googleは同意の選択肢を再度開ける分かりやすい導線を求めています。設定ページに「プライバシー設定」の行を追加し、不要なときは非表示にしてください:
const { showPrivacyEntry } = await startAds();
document.querySelector('#privacy-row')?.toggleAttribute('hidden', !showPrivacyEntry);
document.querySelector('#privacy-row')?.addEventListener('click', () => openPrivacyOptions());
ユーザーが選択を変更した後、以下を読み取ります: canRequestAds 次の広告リクエストの前に再度読み取ります。
ステップ 4: iOS の App Tracking Transparency
ATT は GDPR とは別物です。iOS において ATT は IDFA へのアクセスを制御し、GDPR の同意は個人データの処理の可否と方法を制御します。iOS を使用する EU ユーザーに対しては、通常その両方が必要になります。
- 追加
NSUserTrackingUsageDescriptionまでInfo.plist. - AdMob でメッセージを設定している場合は、UMP に IDFA の説明メッセージを表示させ、ATT をトリガーさせてください。そうでない場合は、同意フローの後に自身で ATT をリクエストしてください。
を使用すると、 @capgo/capacitor-admob ATT のステータスを直接読み取ったり、リクエストしたりするには以下を使用します:
import { AdMob, TrackingAuthorizationStatus } from '@capgo/capacitor-admob';
const { status } = await AdMob.trackingAuthorizationStatus();
if (status === TrackingAuthorizationStatus.notDetermined) {
await AdMob.requestTrackingAuthorization();
}
Capgo には、単体での App Tracking Transparency プラグインも用意されています 他の SDK で ATT が必要な場合に利用できます。プライバシーマニフェストと App Store のプライバシーラベルへのトラッキングの宣言を忘れないでください。詳細は当社の privacy manifest ガイドを.
ステップ 5: デバッグ用地理情報のテスト
UMPは、EEA、英国、またはスイスにいるユーザーにのみGDPRフォームを表示します。それ以外の地域でテストするには:
- アプリを一度実行し、デバイスのハッシュ化されたIDを含むUMPのログラインを探します。
- それをテストデバイスとして追加し、debug geographyをEEAに設定します。コミュニティプラグインを使用する場合は、
debugGeographyとtestDeviceIdentifiersまでrequestConsentInfoを渡します。ネイティブブリッジの場合は、DebugSettingsをiOSで、ConsentDebugSettingsをAndroidで、デバッグビルド時のみ設定してください。 - フォームを再度表示させるために、実行ごとに同意をリセット(
resetConsentInfo()はコミュニティプラグイン、ConsentInformation.reset()はネイティブでの操作)します。
デバッグ設定をリリースビルドに含めないでください。必ずビルドフラグで制御してください。
ステップ 6: アプリを停止させずにエラーを処理する
同意リクエストは、ネットワークがない、App ID の設定ミス、公開済みメッセージがない、といった理由で失敗することがあります。失敗はクラッシュではなく「今は広告を出さない」として扱ってください:
- もし
requestConsentInfoUpdate失敗してもcanRequestAds前のセッションで true だった場合は、引き続き広告をリクエストできます。 - フォームの読み込みに失敗した場合は、ログに記録し、次回の起動時にリトライしてください。
- 同意フォームが表示されている間は、絶対に広告を表示しないでください。
トラブルシューティング
フォームが全く表示されません。 メッセージが未公開、この App ID に割り当てられていない、またはデバッグ用の地域設定なしで EEA 域外にいる可能性があります。次の場所で AdMob App ID を確認してください: Info.plist と AndroidManifest.xml コンソールと一致しているか確認してください。
フォームは起動のたびに表示されます。 ユーザーが選択せずに閉じた場合、または本番環境の code で reset を呼び出した場合です。
canRequestAds 米国ユーザーの場合は false になります。 米国の州法に基づくメッセージを表示し、ユーザーがデータの販売・共有を拒否した場合、SDK を通じて制限付き広告のみを配信してください。バイパスしないでください。
メディエーションパートナーには同意が得られません。 各パートナーのアダプターが最新であり、GDPRメッセージ内の広告パートナーリストに記載されていることを確認してください。
iOSのATT promptが表示されません。 NSUserTrackingUsageDescription が不足しているか、ユーザーが設定で「Appにトラッキングを要求することを許可」を無効にしています。
フォームを超えたコンプライアンス
UMPは広告側を処理します。アプリ側には、AdMobの記載、データ保持ルール、データリクエストへの対応方法を明記したプライバシーポリシーが引き続き必要です。当社の GDPRコンプライアンス・チェックリスト はアプリの残りの部分をカバーします。