個人開発のiOSアプリにAdMob広告を組み込む際、UMP(User Messaging Platform)で同意フローを実装したら、ATTやiOS審査要件と絡んで地雷を3つ踏みました。
- ハマり①: ATTとUMPの 表示順序 を間違えてパーソナライズ広告が出なくなった
- ハマり②: GDPR地域外で動作確認できず「動いていない」と勘違いしてSDK調査に3時間溶かした
- ハマり③: 設定画面から 同意の再表示 ができない実装で審査リジェクトの危機
「広告SDK入れて終わり」ではないので、これから組み込む方の参考になれば。
前提知識: そもそもUMPとは何か
UMP(User Messaging Platform)はGoogleが提供する 同意管理プラットフォーム です。EEA/UK/スイス向けのGDPR同意取得をハンドリングしてくれる仕組みで、AdMobを使う場合は実質必須になっています。
iOS開発者が押さえるべきは 「UMP」「ATT」「AdMob」が3層で絡んでいる という点です。
| 層 | 何をするか | 関連法規/ポリシー |
|---|---|---|
| UMP | EEA向けGDPR同意取得 | GDPR / Google EU User Consent Policy |
| ATT | IDFA(広告識別子)アクセス許可 | Apple App Tracking Transparency |
| AdMob SDK | 広告配信 | Google AdMob ポリシー |
それぞれ要件が違うので、片方だけ実装しても他方の要件を満たせません。
ハマり① ATTとUMPの表示順序問題
何が起きたか
最初、何も考えず以下の順序で実装しました。
// ❌ ATTを先に出してしまった
func didFinishLaunching() async {
// ATTダイアログ表示
let attStatus = await ATTrackingManager.requestTrackingAuthorization()
// UMP同意フォーム表示
await requestUMPConsent()
// 広告SDK初期化
MobileAds.shared.start()
}
EEA地域でテスト(後述)したら、ATTで「Allow」を押したのにパーソナライズ広告が出ない状態に。原因究明に時間を溶かしました。
なぜダメだったか
GoogleのドキュメントとAppleのガイドラインを照らし合わせると、以下の事情があります。
- ATTで「Allow Tracking」を選んでも、UMPでGDPR同意していないと、AdMobはパーソナライズ広告を配信できない
- 逆もしかりで、UMPで同意してもATTで拒否すればIDFAは取得できない
- 両方のクリアが必要で、順序を間違えるとUXが破綻する
特にユーザー視点で考えると、いきなりATTで「トラッキングを許可しますか?」と聞かれた後にGDPR同意フォームが出ると、**「さっきのATTダイアログは何だったの?」**となってチャーンします。
解決策: UMP → ATT → AdMob初期化の順序
Googleが公式に推奨している順序です。
// ✅ 正しい順序
@MainActor
final class AdConsentCoordinator {
static let shared = AdConsentCoordinator()
func bootstrap() async {
// 1. UMP同意フローを先に実行
await requestUMPConsent()
// 2. UMPの結果を踏まえてATTを表示
// (EEAでない地域ではUMPはno-opになる)
if #available(iOS 14, *) {
_ = await ATTrackingManager.requestTrackingAuthorization()
}
// 3. AdMob SDK初期化
if ConsentInformation.shared.canRequestAds {
MobileAds.shared.start()
}
}
private func requestUMPConsent() async {
let parameters = RequestParameters()
parameters.isTaggedForUnderAgeOfConsent = false
do {
try await ConsentInformation.shared
.requestConsentInfoUpdate(with: parameters)
} catch {
print("UMP requestConsentInfoUpdate error: \(error)")
return
}
guard let vc = await topViewController() else { return }
do {
try await ConsentForm.loadAndPresentIfRequired(from: vc)
} catch {
print("UMP loadAndPresentIfRequired error: \(error)")
}
}
}
ポイントは以下の通りです。
- UMP → ATT → AdMob.start() の順を厳守する
-
ConsentForm.loadAndPresentIfRequiredは EEA地域でのみフォーム表示、それ以外では何もせずに完了する -
canRequestAdsがfalseの状態でMobileAds.shared.start()を呼ぶと、非パーソナライズ広告すら表示できない設定になることがある
補足: 非パーソナライズ広告のフォールバック
ユーザーがUMPで「同意しない」を選んだ場合でも、非パーソナライズ広告は配信できます。canRequestAds の判定はその両方を含む形になるので、原則これで分岐すればOKです。
if ConsentInformation.shared.canRequestAds {
// 広告リクエスト可
loadBannerAd()
}
ハマり② GDPR地域外でのテストで詰む
何が起きたか
実装が終わって自分のiPhoneで動作確認しても、UMP同意フォームが一切表示されない。
「コードが間違っているのか?」「SDKのバージョンか?」と3時間ほどデバッグして、ようやく気づきます。
自分は日本にいるのでGDPR適用地域外。同意フォームは原則表示されない。
これ、知らないと気づくのに本当に時間がかかります。SDKは「正常動作」しているので、エラーログも出ません。
解決策: UMPDebugSettings で地域を偽装
UMPには開発・テスト用のデバッグ機能があります。
private func requestUMPConsent() async {
let parameters = RequestParameters()
parameters.isTaggedForUnderAgeOfConsent = false
#if DEBUG
let debugSettings = DebugSettings()
debugSettings.geography = .EEA // EEA地域として動作させる
debugSettings.testDeviceIdentifiers = [
"YOUR-TEST-DEVICE-ID"
]
parameters.debugSettings = debugSettings
#endif
// ... 以下同じ
}
テストデバイスIDの取得方法
testDeviceIdentifiers には自分の端末のIDを入れる必要があります。これも取得方法がやや独特で、一度デバッグ実行してログから拾う という手順を踏みます。
-
requestConsentInfoUpdateを実行 - Xcodeのコンソールに
<UMP SDK>To enable debug mode for this device, set: UMPDebugSettings.testDeviceIdentifiers = @[ @"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" ]のようなログが出る - その UUID を
testDeviceIdentifiersに追加して再ビルド
同意状態のリセット
テスト中、何度も同意フォームを表示したい場面があります。一度同意/拒否すると次回以降フォームが出ないので、リセット用APIを使います。
#if DEBUG
func resetConsentForTesting() {
ConsentInformation.shared.reset()
}
#endif
設定画面の隅に「DEBUG: Reset Consent」ボタンを置いておくと開発が捗ります(リリースビルドでは #if DEBUG で除外)。
ハマり③ 設定画面からの同意再表示が必須
何が起きたか
実装を終えて、「これでいけるだろう」とTestFlightに上げてレビューに回したところ、Apple審査でもAdMobポリシーでも同意の再表示要件が確認される ことを知りました。
具体的には以下の要件があります。
- ユーザーが 後から同意状態を変更できる導線 を提供する必要がある
- 設定画面・プライバシー設定画面などにボタンを置くのが一般的
- ボタン表示条件は GDPR適用地域でのみ表示 が望ましい(地域外で表示すると逆に混乱)
これに気づいたのは審査リジェクトされた後で、修正リリースで対応する羽目になりました。
解決策: 条件付きで再表示ボタンを設置
UMP SDKは「再表示ボタンを出すべきか」の判定APIを持っています。
@MainActor
final class PrivacySettingsViewModel: ObservableObject {
@Published var shouldShowPrivacyOptionsButton: Bool = false
func updateState() {
let status = ConsentInformation.shared.privacyOptionsRequirementStatus
shouldShowPrivacyOptionsButton = (status == .required)
}
func presentPrivacyOptionsForm() async {
guard let vc = topViewController() else { return }
do {
try await ConsentForm.presentPrivacyOptionsForm(from: vc)
// フォーム閉じた後の状態を反映
updateState()
} catch {
print("presentPrivacyOptionsForm error: \(error)")
}
}
}
設定画面のSwiftUI側はこんな感じになります。
struct PrivacySettingsView: View {
@StateObject private var viewModel = PrivacySettingsViewModel()
var body: some View {
Form {
// ... 他の設定項目
if viewModel.shouldShowPrivacyOptionsButton {
Section("広告に関する設定") {
Button("広告のパーソナライズ設定を変更") {
Task {
await viewModel.presentPrivacyOptionsForm()
}
}
}
}
}
.onAppear { viewModel.updateState() }
}
}
privacyOptionsRequirementStatus が .required を返すのは EEA地域のユーザーのみ なので、日本のユーザーには自動的に非表示になります。
App Storeレビューガイドラインとの関係
Appleの審査ガイドライン5.1.1にはトラッキング・データ取扱いの透明性に関する規定があり、ユーザーが同意を後から取り消せる手段の提供 が求められます。UMPの再表示ボタンはこの要件にも対応できる位置付けなので、設定画面に置いておくのは保険として強く推奨 です。
まとめ
AdMobのUMP同意フロー実装で押さえるべきポイントは以下の通りです。
- 表示順序は UMP → ATT → AdMob.start() を厳守する
- GDPR地域外での動作確認は
DebugSettings.geography = .EEAで偽装する - 設定画面からの同意再表示は必須要件。
privacyOptionsRequirementStatusで出し分け
「広告SDKを入れる」と一言で言っても、GDPR / ATT / Apple審査ガイドラインが3層で絡むため、実装よりむしろ要件整理に時間を使う のが現実です。
公式ドキュメントは整っているのですが、それぞれ別ドキュメントに分散しているため全体像が掴みにくい。本記事が「3つの地雷」を先に踏まずに済むためのチェックリストになれば嬉しいです。
次回はこのアプリで採用したCore Data + CloudKit のスキーマ設計について書く予定です。学習アプリ特有の「マスタデータ更新×ユーザー進捗保護」のジレンマで半日溶かした話を共有します。
何か質問・ツッコミがあれば、コメントでお気軽にどうぞ。