1. App Tracking Transparency (ATT)とは
App Tracking Transparency(ATT)は、iOS 14.5以降で導入されたAppleのプライバシー保護機能です。アプリがユーザーのアクティビティを追跡したり、デバイスの広告識別子(IDFA)にアクセスしたりする前に、ユーザーの明示的な許可を得ることを義務付けています。
ユーザーがアプリを初めて起動した際、ATTダイアログが表示され、以下の選択肢が提示されます: - トラッキングを許可:アプリがIDFAにアクセスし、広告目的でのトラッキングが可能になります - Appにトラッキングしないように要求:IDFAへのアクセスが制限され、トラッキングが禁止されます
2. なぜ必要か
コンプライアンスの観点
iOS 14.5以降、ATT対応は必須要件となっています。対応していない場合、以下のリスクがあります:
- App Storeの審査でリジェクトされる可能性:広告SDKを使用している場合、ATT実装なしではアプリが承認されません
- 法的リスク:プライバシー規制違反により、罰金や法的措置の対象となる可能性があります
- ユーザーの信頼性低下:プライバシーを尊重しないアプリとして評価が下がる恐れがあります
ビジネスの観点
ATTへの適切な対応により、以下のメリットが得られます:
- 広告収益の最適化:許可を得たユーザーに対してターゲティング広告を配信でき、広告単価(eCPM)の向上が期待できます
- ユーザー体験の向上:透明性のある許可プロセスにより、ユーザーとの信頼関係を構築できます
- 分析精度の向上:IDFAを使用した正確なユーザー行動分析が可能になります
3. 実際のコード
UnityでATTを実装する際は、Unity Advertisement iOS Support パッケージを使用します。以下、UniTask版とIEnumerator版の両方の実装例を示します。
事前準備
Package Managerから以下のパッケージをインストールしてください: - iOS 14 Advertising Support (com.unity.ads.ios-support)

UniTask版を使用する場合は、UniTaskパッケージも必要です。
3.1 UniTask版
using System; using System.Threading; using Cysharp.Threading.Tasks; using UnityEngine; #if UNITY_IOS using Unity.Advertisement.IosSupport; using UnityEngine.iOS; #endif namespace ATT { /// <summary> /// App Tracking Transparencyの許可リクエストを管理するクラス /// </summary> public sealed class PrivacySystem : MonoBehaviour { /// <summary> /// セットアップ処理 /// </summary> private async void Start() { // ATTリクエスト await RequestTrackingAuthorizationAsync(); } /// <summary> /// ATTリクエスト(非同期処理) /// </summary> private async UniTask RequestTrackingAuthorizationAsync() { try { // エディター環境ではスキップ #if UNITY_EDITOR await UniTask.NextFrame(); Debug.Log("ATT: Skipped in Editor"); return; #endif #if UNITY_IOS // 現在の認証ステータスを取得 var currentAuthorizationStatus = ATTrackingStatusBinding.GetAuthorizationTrackingStatus(); Debug.Log($"ATT Current Status: {currentAuthorizationStatus}"); // 未確認の場合はリクエストダイアログを表示 if (currentAuthorizationStatus == ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED) { Debug.Log("ATT: Requesting authorization..."); ATTrackingStatusBinding.RequestAuthorizationTracking(); } // ステータスが変わるまで待機(最大10秒) var timeout = 10f; var elapsed = 0f; while (currentAuthorizationStatus == ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED && elapsed < timeout) { await UniTask.Delay(TimeSpan.FromSeconds(0.5f)); elapsed += 0.5f; currentAuthorizationStatus = ATTrackingStatusBinding.GetAuthorizationTrackingStatus(); } // 最終ステータスをログ出力 Debug.Log($"ATT Final Status: {currentAuthorizationStatus}"); // ステータスに応じた処理 HandleTrackingStatus(currentAuthorizationStatus); #endif } catch (Exception e) { Debug.LogError("ATT Status Error"); Debug.LogException(e); } } #if UNITY_IOS /// <summary> /// トラッキングステータスに応じた処理 /// </summary> private void HandleTrackingStatus(ATTrackingStatusBinding.AuthorizationTrackingStatus status) { switch (status) { case ATTrackingStatusBinding.AuthorizationTrackingStatus.AUTHORIZED: Debug.Log("ATT: トラッキングが許可されました"); // 広告SDKの初期化など break; case ATTrackingStatusBinding.AuthorizationTrackingStatus.DENIED: Debug.Log("ATT: トラッキングが拒否されました"); // 制限された機能での動作 break; case ATTrackingStatusBinding.AuthorizationTrackingStatus.RESTRICTED: Debug.Log("ATT: トラッキングが制限されています"); // 保護者による制限など break; case ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED: Debug.Log("ATT: ステータスが未決定です(タイムアウト)"); break; } } #endif } }
3.2 IEnumerator版
using System; using System.Collections; using UnityEngine; #if UNITY_IOS using Unity.Advertisement.IosSupport; using UnityEngine.iOS; #endif namespace ATT { /// <summary> /// App Tracking Transparencyの許可リクエストを管理するクラス(コルーチン版) /// </summary> public sealed class PrivacySystemCoroutine : MonoBehaviour { /// <summary> /// セットアップ処理 /// </summary> private void Start() { // ATTリクエストコルーチンを開始 StartCoroutine(RequestTrackingAuthorizationCoroutine()); } /// <summary> /// ATTリクエスト(コルーチン処理) /// </summary> private IEnumerator RequestTrackingAuthorizationCoroutine() { // エディター環境ではスキップ #if UNITY_EDITOR yield return null; Debug.Log("ATT: Skipped in Editor"); yield break; #endif #if UNITY_IOS try { // 現在の認証ステータスを取得 var currentAuthorizationStatus = ATTrackingStatusBinding.GetAuthorizationTrackingStatus(); Debug.Log($"ATT Current Status: {currentAuthorizationStatus}"); // 未確認の場合はリクエストダイアログを表示 if (currentAuthorizationStatus == ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED) { Debug.Log("ATT: Requesting authorization..."); ATTrackingStatusBinding.RequestAuthorizationTracking(); } // ステータスが変わるまで待機(最大10秒) float timeout = 10f; float elapsed = 0f; while (currentAuthorizationStatus == ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED && elapsed < timeout) { yield return new WaitForSeconds(0.5f); elapsed += 0.5f; currentAuthorizationStatus = ATTrackingStatusBinding.GetAuthorizationTrackingStatus(); } // 最終ステータスをログ出力 Debug.Log($"ATT Final Status: {currentAuthorizationStatus}"); // ステータスに応じた処理 HandleTrackingStatus(currentAuthorizationStatus); } catch (Exception e) { Debug.LogError("ATT Status Error"); Debug.LogException(e); } #else yield break; #endif } #if UNITY_IOS /// <summary> /// トラッキングステータスに応じた処理 /// </summary> private void HandleTrackingStatus(ATTrackingStatusBinding.AuthorizationTrackingStatus status) { switch (status) { case ATTrackingStatusBinding.AuthorizationTrackingStatus.AUTHORIZED: Debug.Log("ATT: トラッキングが許可されました"); // 広告SDKの初期化など OnTrackingAuthorized(); break; case ATTrackingStatusBinding.AuthorizationTrackingStatus.DENIED: Debug.Log("ATT: トラッキングが拒否されました"); // 制限された機能での動作 OnTrackingDenied(); break; case ATTrackingStatusBinding.AuthorizationTrackingStatus.RESTRICTED: Debug.Log("ATT: トラッキングが制限されています"); // 保護者による制限など OnTrackingRestricted(); break; case ATTrackingStatusBinding.AuthorizationTrackingStatus.NOT_DETERMINED: Debug.Log("ATT: ステータスが未決定です(タイムアウト)"); OnTrackingUndetermined(); break; } } /// <summary> /// トラッキングが許可された場合の処理 /// </summary> private void OnTrackingAuthorized() { // 広告SDKの初期化 // 例: InitializeAdvertisementSDK(); // IDFAを使用した分析の開始 // 例: StartAnalyticsWithIDFA(); } /// <summary> /// トラッキングが拒否された場合の処理 /// </summary> private void OnTrackingDenied() { // コンテクスチュアル広告への切り替え // 例: UseContextualAdvertising(); // 制限付き分析の開始 // 例: StartLimitedAnalytics(); } /// <summary> /// トラッキングが制限されている場合の処理 /// </summary> private void OnTrackingRestricted() { // 保護者による制限への対応 // 例: HandleParentalRestrictions(); } /// <summary> /// ステータスが未決定の場合の処理 /// </summary> private void OnTrackingUndetermined() { // デフォルトの動作設定 // 例: UseDefaultSettings(); } #endif } }
実装時の注意点
Info.plistの設定
XcodeプロジェクトのInfo.plistに以下のキーを追加する必要があります:
<key>NSUserTrackingUsageDescription</key> <string>このアプリは、より関連性の高い広告を表示するために、あなたのアクティビティを追跡する許可を求めています。</string>
この説明文はATTダイアログに表示されるため、ユーザーにとって分かりやすく、許可することのメリットが伝わる内容にすることが重要です。
タイミングの考慮
ATTリクエストを表示するタイミングは慎重に選ぶ必要があります:
- アプリ起動直後は避ける:ユーザーがアプリの価値を理解する前に許可を求めると、拒否される可能性が高くなります
- コンテキストを提供する:なぜトラッキングが必要なのか、事前に説明画面を表示することで承認率が向上します
- 適切なタイミング:チュートリアル完了後や、広告関連機能を使用する直前など、ユーザーが理解しやすいタイミングで表示します
テスト方法
- 実機でのテスト:シミュレーターではATTの動作が正確に再現されないため、必ず実機でテストしてください
- 設定のリセット:設定 > プライバシー > トラッキング から、アプリごとの許可をリセットできます
- 複数のiOSバージョン:iOS 14.5以降の複数のバージョンでテストすることを推奨します
これらの実装により、Appleのガイドラインに準拠したATT対応が可能になり、ユーザーのプライバシーを尊重しながら、必要な広告機能を維持することができます。