しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Unity で iOS 14 のトラッキング許可ダイアログを表示する

※ これは 2021/04/02 時点の Unity 2020.3.2f1, Xcode 12.4 の情報です

最新版では動作が異なる可能性がありますのでご注意ください

NavMesh を利用した 2D キャラ動作はだいたいやりたいことができたので、今度は別のことを試してみる

ついに始まったらしいこれについて

2021年4月末までの対応必須化が噂されるため iOS 14 の App Tracking Transparency 対応 を Unity で実装してみたい

まずは下記のように Bootstrap という名前で初期シーンの Hierarchy 上に GameObject を追加

Bootstrap の GameObject

この GameObject には下記のような Bootstrap.cs というスクリプトをアタッチする

using UnityEngine;

public class Bootstrap : MonoBehaviour
{
    /// <summary>
    /// 開始処理
    /// </summary>
    public async void Start()
    {
#if UNITY_IOS && !UNITY_EDITOR

        var status = NativeService.GetIOSTrackingAuthorizationStatus();

        // 未回答だった場合は ATT 許可要求ダイアログを表示
        if (!status.HasValue)
        {
            status = await NativeService.RequestTrackingAuthorization() as bool?;
        }

        if (status.Value)
        {
            //TODO: 許可時の処理
        }
        else
        {
            //TODO: 不許可時の処理
        }
#endif
    }
}

アプリ起動時に ATT 許可状態を調べて、未回答だった場合許可要求ダイアログを表示して結果に応じた分岐をさせるだけ

ここで呼び出されている NativeService.cs は下記のような感じ

using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

public class NativeService
{
    private static TaskCompletionSource<bool> AttTcs;
    private static SynchronizationContext Context;

    static NativeService()
    {
        Context = SynchronizationContext.Current;
    }

#if UNITY_IOS

    /// <summary>
    /// ATT 許可状態取得
    /// </summary>
    /// <returns></returns>
    [DllImport("__Internal")]
    private static extern int GetTrackingAuthorizationStatus();

    private delegate void OnCompleteStatusCallback(int status);

    /// <summary>
    /// ATT 許可要求
    /// </summary>
    /// <returns></returns>
    [DllImport("__Internal")]
    private static extern int RequestTrackingAuthorization(OnCompleteStatusCallback callback);

    /// <summary>
    /// ATT の同意状態を取得する
    /// </summary>
    /// <returns></returns>
    public static bool? GetIOSTrackingAuthorizationStatus()
    {
#if UNITY_EDITOR
        return true;
#else
        switch (GetTrackingAuthorizationStatus())
        {
            case -1: // No Needs
            case 3: // Authorized
                return true;

            case 0: // Not Determined
                return null;

            default:
                return false;
        }
#endif
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public static Task<bool> RequestTrackingAuthorization()
    {
#if UNITY_EDITOR
        return Task.FromResult(true);
#else
        AttTcs = new TaskCompletionSource<bool>();

        RequestTrackingAuthorization(OnRequestTrackingAuthorizationComplete);
        return AttTcs.Task;
#endif
    }

    [AOT.MonoPInvokeCallback(typeof(OnCompleteStatusCallback))]
    private static void OnRequestTrackingAuthorizationComplete(int status)
    {
        if (AttTcs != null)
        {
            Context.Post(_ =>
            {
                switch (status)
                {
                    case -1: // No Needs
                    case 3: // Authorized
                        AttTcs.TrySetResult(true);
                        break;

                    default:
                        AttTcs.TrySetResult(false);
                        break;
                }
            }, null);
        }
    }

#endif
}

iOS のネイティブプラグインを呼び出して戻り値を橋渡ししている

さらにこの NativeService が呼び出すプラグインスクリプトNativeService.mm という名前で Assets/Plugins/iOS ディレクトリ配下に作成

プラグインスクリプト

中身はこんな感じ

#import <Foundation/Foundation.h>
#import <AppTrackingTransparency/ATTrackingManager.h>

#ifdef __cplusplus
extern "C" { 
#endif

// ATT 許可状態取得
// 0: Not Determined, 1: Restricted, 2: Denied, 3: Authorized, -1: No Needs
int GetTrackingAuthorizationStatus(){
    if (@available(iOS 14, *)) {
        return (int)ATTrackingManager.trackingAuthorizationStatus;
    } else {
        return -1;
    }
}

typedef void (*Callback)(int status);

// ATT 許可要求
// 0: Not Determined, 1: Restricted, 2: Denied, 3: Authorized, -1: No Needs
void RequestTrackingAuthorization(Callback callback)
{
    if (@available(iOS 14, *)) {
        [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
            if (callback != nil) {
                callback((int)status);
            }
        }];
    } else {
        callback(-1);
    }
}

#ifdef __cplusplus
}
#endif

あとは iOS のビルドだけ・・・だけども Xcode 用のプロジェクトを出力後、中身を開いて追加の作業手順が必要

UnityFramework のターゲットプロジェクトを選択し、AppTrackingTrasparency.framework への参照を追加する

AppTrackingTrasparency.framework を参照に追加

さらに Unity-iPhone のターゲットプロジェクトを選択し、Info.plist に Privacy - Tracking Usage Description を追加

Privacy - Tracking Usage Description を追加

中身は収集するユーザーデータの利用目的を記載していないと Apple の審査でリジェクトされる

「上記は広告表示や利用状況の分析に使用します」という文言にした

ここまでやったらビルドして iPhone の実機で動かしてみると・・・

ATT 許可要求ダイアログ

ちゃんと表示されたね