iOS アプリで Google AdMob UMP(同意管理)を統合する

Google Mobile Ads SDK と一緒に UMP(User Messaging Platform) を組み込み、GDPR(EEA・英国・スイス)と米国州法(CCPA など)の同意要件を満たすための実装と AdMob コンソール作業のまとめ。Swift / SwiftUI を前提とするが、考え方は UIKit でも同じ。

目次
  1. UMP とは / 何を解決するか
  2. 全体フロー
  3. SDK 導入(SPM)
  4. コード実装
    1. ConsentManager の責務分離
    2. 起動時フロー
    3. 設定画面のエントリポイント
    4. 子ども向けアプリの注意点
  5. AdMob コンソール側の作業
    1. 米国の州メッセージ
    2. GDPR メッセージ
    3. GDPR アカウント設定
  6. App Store Connect 側
  7. ハマりどころ・トラブルシューティング
  8. 動作確認のコツ
  9. 参考リンク

1. UMP とは / 何を解決するか

UMP(User Messaging Platform)は Google が提供する 同意管理 SDK で、AdMob・Google 広告を使うアプリが各地域の同意要件を自動で満たすための仕組み。

ポイント: UMP は 地域判定をランタイム(IP ベース)で行うので、単一バイナリで全地域に対応できる。EU 向けに別ビルドを作る必要はない。

UMP を使わないとどうなるか

2. 全体フロー

アプリ起動
  └─ ConsentManager.requestConsentInfoUpdate()  ← UMP に問い合わせ
       ├─ 必要なら同意フォーム表示(地域判定済み)
       └─ 取得後 MobileAds.start()             ← 広告 SDK 初期化

設定画面
  └─ 「広告データの設定」ボタン
       └─ presentPrivacyOptionsForm()  ← EEA / 米国州ユーザーが同意を変更

順序が重要: 同意取得が 完了してから MobileAds.start() を呼ぶこと。逆順だと「同意なしで初期化」したことになり Google ポリシー違反扱いになりうる。

3. SDK 導入(SPM)

Xcode → File → Add Package Dependencies で以下のいずれかを追加。

パッケージ含まれる SDK
github.com/googleads/swift-package-manager-google-mobile-adsGoogleMobileAds + GoogleUserMessagingPlatform(UMP は依存で同梱)
github.com/googleads/swift-package-manager-google-user-messaging-platformUMP のみ(UserMessagingPlatform)

広告も配信するアプリなら前者でまとめて入る。SDK のライブラリ名は パッケージ経由で GoogleUserMessagingPlatform、単独パッケージで UserMessagingPlatform と異なる点に注意。両対応するなら canImport でガードする:

#if canImport(UserMessagingPlatform)
import UserMessagingPlatform
#elseif canImport(GoogleUserMessagingPlatform)
import GoogleUserMessagingPlatform
#endif

4. コード実装

4.1. ConsentManager の責務分離

UMP API を直接 View / App から呼ぶと条件付きコンパイルや状態管理が散らかる。専用クラス(ConsentManager など)にラップするのが定石。

import Foundation
import os
import Observation
#if canImport(UIKit)
import UIKit
#endif
#if canImport(UserMessagingPlatform)
import UserMessagingPlatform
#elseif canImport(GoogleUserMessagingPlatform)
import GoogleUserMessagingPlatform
#endif

@Observable
final class ConsentManager {
    static let shared = ConsentManager()
    private init() {}

    /// 設定画面で「広告データの設定」ボタンを表示すべきか
    var canPresentPrivacyOptions: Bool = false

    #if os(iOS)

    static func keyRootViewController() -> UIViewController? {
        UIApplication.shared.connectedScenes
            .compactMap { ($0 as? UIWindowScene)?.windows.first?.rootViewController }
            .first
    }

    /// アプリ起動時に呼ぶ。完了後に completion を呼ぶ。
    func requestConsentIfNeeded(from rootVC: UIViewController?,
                                 completion: @escaping () -> Void) {
        #if canImport(UserMessagingPlatform) || canImport(GoogleUserMessagingPlatform)
        let params = RequestParameters()
        // 子ども向けアプリの場合のみ true。それ以外は false(既定)
        // params.isTaggedForUnderAgeOfConsent = true

        ConsentInformation.shared.requestConsentInfoUpdate(with: params) { [weak self] error in
            if let error {
                // ログだけ残して続行(オフライン時など)
                self?.refreshAvailability()
                completion()
                return
            }
            guard let rootVC else {
                self?.refreshAvailability()
                completion()
                return
            }
            ConsentForm.loadAndPresentIfRequired(from: rootVC) { [weak self] _ in
                self?.refreshAvailability()
                completion()
            }
        }
        #else
        completion()
        #endif
    }

    func presentPrivacyOptionsForm(from rootVC: UIViewController) {
        #if canImport(UserMessagingPlatform) || canImport(GoogleUserMessagingPlatform)
        ConsentForm.presentPrivacyOptionsForm(from: rootVC) { _ in }
        #endif
    }

    private func refreshAvailability() {
        #if canImport(UserMessagingPlatform) || canImport(GoogleUserMessagingPlatform)
        canPresentPrivacyOptions =
            ConsentInformation.shared.privacyOptionsRequirementStatus == .required
        #endif
    }

    #endif
}

API 名のリネームに注意: Google Mobile Ads SDK 13.x 以降の UMP は Objective-C 由来の UMP プレフィックス API(UMPConsentInformation など)から Swift 命名(ConsentInformation.sharedparams.isTaggedForUnderAgeOfConsent)にリネームされている。古い記事のコードはそのまま動かない。

4.2. 起動時フロー

SwiftUI なら WindowGroup 配下のルート View に .task を付け、UMP → 広告 SDK の順で起動する:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            RootView()
                .task { startConsentAndAds() }
        }
    }

    private func startConsentAndAds() {
        if AdService.shared.isInitialized { return }
        #if os(iOS)
        let rootVC = ConsentManager.keyRootViewController()
        ConsentManager.shared.requestConsentIfNeeded(from: rootVC) {
            AdService.shared.configure()  // ここで MobileAds.start
        }
        #else
        AdService.shared.configure()
        #endif
    }
}

4.3. 設定画面のエントリポイント

GDPR / 米国州法は「ユーザーが同意を後から変更できる導線」を要求する。設定画面に「広告データの設定」「プライバシー設定」などのボタンを置き、UMP のフォームを再表示できるようにする:

Section("プライバシー") {
    if ConsentManager.shared.canPresentPrivacyOptions {
        Button {
            guard let rootVC = ConsentManager.keyRootViewController() else { return }
            ConsentManager.shared.presentPrivacyOptionsForm(from: rootVC)
        } label: {
            Label("広告データの設定", systemImage: "hand.raised")
        }
    }
}

canPresentPrivacyOptions(=privacyOptionsRequirementStatus == .required)で出し分けることで、EEA / 米国対象州のユーザーにだけ表示される。日本ユーザーには表示されない。

4.4. 子ども向けアプリの注意点

13 歳未満をターゲットとするアプリ(Apple Kids Category / Family Designed for Kids)では:

5. AdMob コンソール側の作業

コードだけでは不十分で、AdMob 管理画面で同意メッセージを作成・公開する必要がある。場所は:

AdMob 左メニュー
└─ プライバシーとメッセージ
   ├─ 欧州の規制(GDPR)       ← 作成 / 公開
   ├─ 米国の州の規制(CCPA 等) ← 作成 / 公開
   └─ IDFA 説明メッセージ      ← ATT 使うときのみ

5.1. 米国の州メッセージ

  1. 「米国の州の規制」→ 「作成」
  2. テンプレート(My data preferences)から「同意のみ」または「Don't sell or share my data」を選択
  3. 対象アプリを選択(後述の「アプリを選択」ダイアログでプライバシーポリシー URL も入力)
  4. 言語を追加(日本語 + 英語など)
  5. 公開
  6. 「エントリ ポイント」タブは触らなくて OK(§7 参照)

5.2. GDPR メッセージ

  1. 「欧州の規制」→ 「作成」
  2. 標準は IAB TCF v2.2 形式(200 社超のパートナー一覧)。子ども向けアプリでも UMP がサーバ側で under-age 用に切り替えるのでこのテンプレで OK
  3. 右パネル「ユーザーの選択」で 「同意しない」を必ずオン(CNIL 解釈で実質必須)
  4. 「アプリを選択」ダイアログでアプリ選択 + プライバシーポリシー URL を入力
  5. デフォルト言語と追加言語を設定
  6. 右上「公開」ボタンが青くなったらクリック

5.3. GDPR アカウント設定(メッセージとは別)

欧州の規制 → 「設定」タブには、メッセージとは別のアカウント全体の挙動を制御する項目がある。子ども向け / 厳格運用なら:

項目推奨理由
正当な利益のコントロールオフ明示同意のみで運用。CNIL 等で安全
広告目的の同意モードオンUMP SDK と Google 広告サービスの連携に必須
特殊機能 2(デバイススキャン)オフ子ども向けやプライバシー重視なら不要
同意の同期オフ複数アプリで同期しないなら off
広告パートナーを自動的に追加オン収益機会を逃さない

6. App Store Connect 側

7. ハマりどころ・トラブルシューティング

TS-1: プライバシーポリシー URL は AdMob のどこに入れるか

設定場所: プライバシーとメッセージ → 欧州の規制(または 米国の州の規制) → メッセージ編集 → 全般設定 → アプリ → 「アプリを選択」ボタン → 開いたダイアログ内に、アプリごとの行に「プライバシー ポリシーの URL」列がある。ここに URL を入れる。

誤探索しやすい場所:

AdMob はプライバシーポリシー URL を「アプリ単位、メッセージ編集時の選択ダイアログ内」で管理している。

TS-2: 「エントリ ポイント」タブで「実装する必要があります」表示が消えない

コード側で presentPrivacyOptionsForm を SettingsView に組み込んでも、AdMob のエントリポイントタブが「未実装」表示のまま。

結論: このタブは SDK のランタイム通信ベースで Google が検知するが、反映に大幅なラグがある。ストア公開も必須ではない。表示が消えなくてもメッセージの公開は可能。実装完了の判定にはこのタブを使えない。

TS-3: 「公開」ボタンが灰色のまま押せない

必須項目の未入力で公開ボタンが活性化しない。よくある原因:

TS-4: SDK API リネームでビルドエラー

古い記事のコードをコピーすると UMPRequestParameters など Obj-C 由来の名前で書かれており、現行 SDK ではビルドが通らない。Xcode のリネーム提案に従って:

旧 API(Obj-C)新 API(Swift)
UMPRequestParametersRequestParameters
params.tagForUnderAgeOfConsentparams.isTaggedForUnderAgeOfConsent
UMPConsentInformation.sharedInstanceConsentInformation.shared
UMPConsentForm.loadAndPresentIfRequiredConsentForm.loadAndPresentIfRequired
UMPConsentForm.presentPrivacyOptionsFormConsentForm.presentPrivacyOptionsForm

TS-5: import UserMessagingPlatform でビルド失敗

SPM で GoogleMobileAds 経由で UMP が入ると、ライブラリ名が GoogleUserMessagingPlatform になる。import UserMessagingPlatform 単独だと解決できないことがある。

対応: 両方の canImport でガードする(§3)。

TS-6: 子ども向けアプリで IAB TCF v2.2 を出してよいか

IAB TCF テンプレは「200 社超のパートナーへのデータ共有」を求める文言。子ども向けには不適切に見える。

結論: UMP は isTaggedForUnderAgeOfConsent = true を受け取ると、サーバ側で under-age ユーザーに対して最小限の表示か非表示に切り替える。テンプレ自体はそのままでよい。ただしアカウント設定で「正当な利益オフ / 特殊機能 2 オフ」にして厳格化しておくと安心。

8. 動作確認のコツ

日本のシミュレーターでは UMP フォームが出ないので、デバッグ設定で地域を強制する:

let debug = DebugSettings()
debug.geography = .EEA  // .EEA / .other / .disabled
debug.testDeviceIdentifiers = ["XXXX-XXXX-..."]  // Xcode コンソールに出る ID
params.debugSettings = debug

9. 参考リンク