Flutter(iOS) App Tracking Transparency(ATT)への最低限の対処法(AdMobの場合)

※当サイトは、アフィリエイト広告を利用しています

Flutter

(2022.6.3追記)

Flutterで開発したアプリをApple審査に出したら、以下の理由でリジェクトされました。

 

Guideline 5.1.2 - Legal - Privacy - Data Use and Sharing

The app privacy information you provided in App Store Connect indicates you collect data in order to track the user, including Performance Data, Product Interaction, Device ID, Crash Data, Coarse Location, and Advertising Data. However, you do not use App Tracking Transparency to request the user's permission before tracking their activity.

Starting with iOS 14.5, apps on the App Store need to receive the user’s permission through the AppTrackingTransparency framework before collecting data used to track them.This requirement protects the privacy of App Store users.

 

大まかに言うと、

 

ユーザーの履歴を追跡する場合は、事前にユーザーから同意を得る仕組みを実装しなさい

 

とのことです。

 

一般には、App Tracking Transparency(ATT)対応と言われているようです。

 

AdMobの広告を入れていたので、「Appのプライバシー」のところでは、ユーザーのトラッキングデータを取得する旨を登録していました。

 

 

iOSでは、広告識別ID(いわゆる「IDFA:Identifier for Advertisers」)を取得する際に、ATT対応が必要になった、という話は耳にしていたのですが、気が及ばず何の対応をしていませんでした。。

 

調べたところ、

 

Flutterのパッケージ「app_tracking_transparency」を導入する

 

で解決できると分かりました。

 

本パッケージの導入については、大変参考になる記事が複数あるので(ありがとうございます!)、分かる方は下記の参考記事で十分かと思います。

  

ただ、不慣れな自分は、何箇所かツマづいたので、、、以下では、その過程を共有できればと思います。

 

 

 

 

(2021.12.7、2022.6.3追記)iOS 15.1対応について

Appleの審査で、「iOS 15.1だとATTの同意取得ダイアログが表示されない」と言われてリジェクトされるケースがあるので、その場合の対処をこちらに簡単にメモしています。併せてご参考になればと思います。

 

 


40代からプログラミング(Flutter)を始めて、GooglePlayAppStoreにアプリを公開しているhalzo appdevです。

 

作成したアプリはこちら↓ 全てFlutterで開発したアプリです。

 

前提とする状況

アプリに導入する広告はAdMobで、AdMob自体の実装は完了済の状態を前提としています。

 

自分が導入したAdMobのパッケージ、バージョンは以下のとおりです。

 

google_mobile_ads: ^0.12.2

 

「app_tracking_transparency」パッケージの導入

ATT対応には、こちらのパッケージを導入します。

 

 

pubspec.yamlファイルの「dependencies:」の箇所に、以下の内容を追記して、Android Studioの画面右上の「Pub get」を押します。

 

dependencies:
  app_tracking_transparency: ^2.0.0

  

※自分は、null safety対応後の最初のバージョンである2.0.0にしましたが、この記事作成時点の最新版は、2.0.1でした。バージョン間で特に大きな変更は無いようなので、今から入れる方は最新版が良いかと思います。

 

SDKのバージョン確認方法

「app_tracking_transparency」パッケージの「Readme」には、Googleが、Google Mobile Ads SDKのバージョン7.64.0以上を使用することを推奨している旨が書かれています。

 

詳しく理解できていないですが、SDKのバージョンが7.64.0以上だと、仮にユーザー同意が得られず、IDFAを取得できなくても、アプリのインストールに関して一定の情報取得が可能、という事らしいです(後述)。

 

そのため、収益化を狙うなら、バージョンは7.64.0以上にしたほうが良いですね。

  

早速、自分が導入しているSDKのバージョンを確認しようとしたのですが、方法が分からず、若干ハマりました。。

 

調べたところ、以下2通りの確認方法があるようです。

 

ターミナルで「pod outdated」する

ターミナルから「ios」のフォルダに移り、「pod outdated」と打つと、下記のような画面が出て、確認できます。

 

 

自分の場合は、「Google-Mobile-Ads-SDK 7.69.0」でした。

 

バージョンが7.64.0以上でなかった場合は、最新版のgoogle_mobile_adsのパッケージをインポートすれば、解消されるかと思います。

 

AdMobの管理画面で「GMA SDK」を表示させる

AdMobの管理画面に行き、「AdMob ネットワークレポート」の画面右下のディメンションの中の「GMA SDK」を選択すると、一覧にバージョンが表示されます。

  

 

ただ、この方法は既にiOSアプリをリリースして、AdMobとの紐付けが完了している必要があるので、リリース前には使えないかもしれません。

 

バージョンが同じ「google_mobile_ads」を使って、既に他のiOSアプリをリリースしている場合は、参考にそちらのアプリでチェックできると思います。

 

info.plistへの追記

「app_tracking_transparency」パッケージver2.0.0の「Readme」には、iosフォルダ→Runnerフォルダにある「info.plist」に、以下2つの追記が必要と書かれています。

 

ユーザー同意画面の表示文言の設定

以下の2行を追記します。

 

<key>NSUserTrackingUsageDescription</key>
<string>利用目的:お客様の興味関心に合わせた広告を表示するために利用いたします。</string>

 

追記する場所は、「info.plist」の上から4行目あたり(何行目かは人によって若干違うかもしれません)にある<dict>と、下から2行目にある</dict>の間であれば、どこでも大丈夫です。

 

2行目の<string>の内容は、ユーザーに表示する文言なので自由に設定可能です。

 

但し、ユーザーに強く同意を促すような文言にすると、Appleの審査でリジェクトされることもあるようなので、なるべくフラットな表現が良いようです。

 

どのような文章ならリジェクトされないか、については、下記記事に多くの事例があり、大変参考になりました。ありがとうございます!

 

 

SKAdNetworkの設定

前述のとおり、Google Mobile Ads SDKのバージョンが7.64.0以上であれば、IDFAを取得できなくても、一定のユーザーデータをトラッキング可能であり、それを有効化するために、下記7行を追記します。

 

<key>SKAdNetworkItems</key>
<array>
<dict>
<key>SKAdNetworkIdentifier</key>
<string>cstr6suwn9.skadnetwork</string>
</dict>
</array>

 

これも追記する場所は、どこでも大丈夫です。

 

「SKAdNetworkItems」は、元々Apple側で用意していたツールで、どのアプリからインストール(コンバージョン)に繋がったかを計測できるらしく、これによりIDFAが取得できなくても一定のトラッキングが可能になります。

 

「SKAdNetworkItems」については、下記記事で大変わかりやすく説明されています。

 

 

ただ、「google_mobile_ads」パッケージの説明にも、上記7行を追記することが指示されているので、AdMobを実装済であれば、追記済のことが多いと思います(自分も追記済でした)。

 

実装方法(追記するコードと追記場所)

AdMobを入れている場合は、アプリ起動時にAdMobの初期化メソッドを呼ぶ箇所(ここでは、「adManagerO..initAdMobO()..initBannerAdO();」と仮定)があるはずなので、その部分に以下のように追記します。

 

※自作したプロパティ・メソッド・クラス名(名称を自由に決められるもの)については、パッケージで規定されている名称と区別できるよう、末尾に大文字のオー「O」(Originalの略の意味)を付けています。

 

AdMobの初期化メソッドを呼ぶ部分に追記するコード
// ATT対応の同意ダイアログ処理のメソッドを呼び出し、その処理後に、AdMobの初期化処理をする
// ※メソッドからの返り値は使わないため、thenメソッドの引数は(_)とする
AttO.attInstanceO.requestPermissionO().then((_) {  // ←追記部分

  // AdMobとバナーの初期化メソッドの呼び出しをする部分
  // 本記事ではこのメソッドのコード説明は割愛
  adManagerO..initAdMobO()..initBannerAdO();

});   // ←追記部分

adManagerO:広告表示を管理するために作成したアドマネージャークラス(本記事ではコード説明を割愛)のインスタンス
initAdMobO()initBannerAdO():それぞれアドマネージャークラス内に定義したAdMob初期化メソッド、バナー広告初期化メソッド(本記事ではコード説明を割愛)
requestPermissionO:ユーザー同意ダイアログを表示して、許可されればIDFAを取得するメソッド

 

次に、ATT対応の処理を書くクラス(requestPermissionOメソッドを含む)を、以下のように作成します。

 

ATT対応用クラスのコード
class AttO {

  // クラス外からアクセスできるように static でインスタンスを作成
  static final AttO attInstanceO = AttO();

  // ユーザー同意ダイアログを表示して、同意されればIDFAを取得するメソッド
  Future requestPermissionO() async {

    // OSがiOSのときのみ実行
    if (Platform.isIOS) {

      // ユーザーが回答済か否か(つまり初回起動か否か)の状態を取得
      TrackingStatus trackingStatusO =
      await AppTrackingTransparency.trackingAuthorizationStatus;
      debugPrint("trackingStatusO:$trackingStatusO");

      try {

        // ユーザーが未回答の場合だけ(=初回起動の場合だけ)、以下の処理を実行
        if (trackingStatusO == TrackingStatus.notDetermined) {

          // 事前説明のダイアログを出したい場合は、この部分にshowDialogメソッド等を入れて
          // 追加のダイアログ表示の処理を書く
          // 【注】他のユーザー同意ダイアログ(通知許可など)の後に、ATTの同意ダイアログを出す場合は、
          //    この事前説明ダイアログ入れたほうが良い(後述)
          // showDialog(context: context, ・・・・

          // ユーザー同意ダイアログを表示して、回答(同意・不同意)の結果を取得
          var statusO =
          await AppTrackingTransparency.requestTrackingAuthorization();
          debugPrint("requestTrackingAuthorization:$statusO");
        }

      // エラー時の処理
      } on PlatformException {
        debugPrint('PlatformException was thrown');
      }
    }

    // IDFAを取得
    // ユーザー不同意の場合は「00000000-0000-0000-0000-000000000000」になる
    final uuidO = await AppTrackingTransparency.getAdvertisingIdentifier();
    debugPrint('IDFA:$uuidO');
  }
}

 

ATT対応は、iOSのときだけ必要になるので、最初にOS判定をしています。

 

次に、同意取得ダイアログの表示は初回起動時のみのため、パッケージに用意されている「trackingAuthorizationStatus」プロパティを用いて、「ユーザーが同意・不同意を回答済か否か」を取得します。

 

このプロパティを使い、回答済なら、同意取得のダイアログを出さずにスルーし、未回答ならダイアログを出すようにします。

 

同意取得のダイアログは、requestTrackingAuthorizationメソッドで呼んでいます。自分でshowDialogメソッドを使って作成する必要はありません。

 

追記するコードはこれだけで、AdMobのバナーをロードしたり表示したりする部分には一切手を加える必要はありません。とても便利なパッケージです。

 

同意取得ダイアログが表示された状態

 

※後述のとおり、同意取得ダイアログの前に、事前説明のダイアログを表示したい場合は、自分でshowDialogメソッド等を使って実装する必要があります。

 

(2021.12.7追記)

注意点として、Appleの審査で、「iOS 15.1だとATTの同意取得ダイアログが表示されない」と言われてリジェクトされるケースがあります。

 

原因は、iOS 15.1から、アプリがアクティブ状態でないとATTのダイアログが表示されないように仕様が変わったためのようです。

 

そのため、アプリ起動前に表示しようとすると、この問題に引っかかるので、「WidgetsBinding.instance?.addPostFrameCallback」メソッドを使うなどして、画面のビルド後に、ダイアログの表示メソッド(上のコード例で言えば「AttO.attInstanceO.requestPermissionO()」メソッド)を呼ぶようにすると確実です(よろしければ、こちらのメモ記事もご参考にしてください)。

 

(2022.6.3追記)

理解不足だったのですが、iOS15.1以降は、ダイアログ表示中=アプリが非アクティブ状態と判定されるため、ユーザー同意を得るダイアログが連続すると後のダイアログが表示されない問題が生じるようです。

 

自分は、アプリ起動時に、通知許可を求める同意ダイアログの直後に、ATTの同意ダイアログを出そうとして、「iOS15.5だとATT同意ダイアログが表示されないよ」と言われて、Apple審査でリジェクトされてしまいました。。。

 

こちらの記事に、詳細経緯を記載しました。

 

 

この場合、ATTの同意ダイアログを表示する前に、showDialogメソッド等を用いて、自作のダイアログを表示させることで、解消できます。

 

※showDialogメソッドによるダイアログの表示中は、アプリはアクティブ状態と判定されるため。

 

ATTへの同意を強く誘導する内容のダイアログを出すと、Apple審査でリジェクトされるようなので、自分の場合は、下記のようなシンプルなダイアログ(下記例は、「CupertinoAlertDialog」)を挟むことで、問題なく、再審査をパスすることができました。

 

 

実装時の留意点

勝手に自分で混乱してしまったのですが、以下の2つは、やろうと思えばできる、というだけで最低限の対処をする上では不要でした。

 

  • 同意取得のダイアログの前に、別途、事前説明のダイアログを出すこと
  • 拒否されたときに広告を非表示にすること

 

1点目は、「今からIDFA取得同意のダイアログを出しますよ」という事前説明のダイアログを表示することで、ユーザーの理解を深め、同意を得やすくするという方法です。

 

これをする場合は、requestTrackingAuthorizationメソッドを呼ぶ前に、showDialogメソッドなどで、ダイアログを自作して、説明を表示します。

 

AdMobでも説明表示を推奨しているためか、事前説明ダイアログを入れる例を多く見かけたので、勘違いしてしまいましたが、必須ではありませんでした

 

(2022.6.3追記)但し、前述のとおり、同意ダイアログを連続させる場合は、ATT同意ダイアログが表示されなくなる問題を回避するため、事前説明ダイアログを入れたほうが良いと思います。

 

2点目は、ユーザーからIDFA取得の同意を得られなかった場合に、そのまま広告を表示したらまずいのではないか?、何か処理を入れなければならないのではないか?という自分の勝手な懸念だったのですが、それは不要でした。

 

不同意の場合は、「app_tracking_transparency」パッケージの方でIDFAを広告側に渡さないよう、よしなにやってくれるようです(詳しい仕組みは理解していないのですが、、)。改めてとても便利なパッケージですね。

 

Appleに再申請するときの留意点

実装後、Appleに再申請しようとしたのですが、ここでもいくつか詰まった箇所があったので、共有したいと思います。

 

「Review Notes」に同意ダイアログの配置場所を報告

Appleからのリジェクトメールをよく読むと、「Next Step」と書かれた項目の中に、

 

When you resubmit, indicate in the Review Notes where the permission request is located.

(再申請するときは、「Review Notes」に、許可リクエストを配置した場所を示してください。)

 

とサラッと書かれていました。

 

要件不足で再リジェクトされると困るので、対応しようとしたのですが、「Review Notes」の場所がわからず困りました。。

 

色々調べた結果、恐らく下図の場所だと判断し、ここに記入して再申請したら1回でパスできたので、恐らく間違っていなかったと思います。

 

 

拙い英語になりますが、日本語と併記して上記のように記入しました。これでリジェクトされなかったので、一応、通じたようです(汗)。

  

「アクティビティ」タブがない

ATT対応とは直接関係ないのですが、アップロードした修正版を再申請するにあたり、Appleのサイトの説明を確認したところ、

 

アップロードしたビルドはすべてApp Store Connectの「マイ App」の「アクティビティ」セクションに表示され、TestFlightまたはApp Storeでの配信を選択することができます。

https://developer.apple.com/jp/support/app-store-connect/

  

と書かれていました。

  

しかし、この「アクティビティ」セクションがどこにあるのか分からず、地味にハマりました。

 

色々調べると、本来、「TestFlight」タブの右側に「アクティビティ」タブがあるようなのですが、自分の場合はその部分が「Xcode Cloud」になっており、それをクリックしてもビルドの一覧らしきものは出てきませんでした。

 

 

結果、「アクティビティ」セクションを表示することは諦め、「App Store」タブのページで、リジェクトされたビルドの右側の「ー」ボタンを押して削除したら、アップロードした修正版のビルドを選択できるようになりました。

 

ただ、「ー」ボタンを押すと、「Resolution Center」というページにあったAppleからのリジェクトメールの内容も消えてしまい、そのページからAppleに返信したいと思っていたのに、できなくなってしまいました。

 

そのため、「Resolution Center」からAppleに返信をしたい場合は、「App Store」タブのページでビルドを削除する前に対応した方がよさそうです。

 


 

こんな感じで、多少は右往左往しましたが、ATT対応を実装し、再申請は1回でパスすることができました。

 

以上、ご参考になれば幸いです。

 

最後までお読みいただき、ありがとうございました。

 

個人アプリ開発で役立ったもの

おすすめの学習教材

超初心者向けでオススメな元Udemyの講座/

 

 \キャンペーン時を狙えば安価で網羅的な内容が学べる(日本語訳あり)/

 

\Gitの基礎について無料で学べる/

 

おすすめの学習書籍

実用的image_pickerに関してかなり助けられた/

 

Dartの基礎文法を素早くインプットできる/


Dart入門 - Dartの要点をつかむためのクイックツアー

コメント

  1. むらしゅう より:

    自分もFlutterでAdmobを実装したアプリをリリースし、リリース後にATT対応の必要が出たので本記事の内容を参考にさせていただきました。
    本当によくまとまっており、非常に参考になりました。
    ありがとうございます!

    • halzo appdevhalzo appdev より:

      むらしゅう様
      コメントいただき、ありがとうございます!
      ATT対応のお役に立てて良かったです!自分が苦労した点を共有して、同じ問題にぶつかった方の少しでもお役に立てればと思って書いております。
      自分も学びながらなので、理解不足で間違ったことを書いてしまうこともあるかと思いますが、気付き次第修正しますので、ご容赦くださいませ^^;
      今後ともよろしくお願いいたします。

タイトルとURLをコピーしました