[Unity2023] Android 33クラッシュ解決,  iOS 14ビルド対応したのでやり方まとめた。🤯🍄🫚

Android 33 と iOS 14 に対応するの、すごく疲れた。
Unity 2019 を使っていたが、Unity 2023 に変更した。


`さらに修正 2023/11/26` ATT ダイアログをIDFA ダイアログに変更👇

https://qiita.com/kaedeee/items/1f9eaa823e58f5fddc62

# 🏕️ Environment

Unity 2023.2.1.f1
`Gradle が標準で最新版が使える方が今後良い為`

Easy Mobile Pro v2.19.0
`Easy mobile Pro の最新版がこれ、すでに廃止されている`

GoogleMobileAds-v7.3.1
`Easymobile Pro の最新版と互換性のあるメソッド名を使っているもののうち最新版だったので`

MetaAudienceNetworkUnityAdapter-3.9.1
`公式がGoogleMobileAds-v7.3.0 でテスト確認済みの為`

com.google.play.review-1.8.1
`Easy mobile Pro の in-app-review が古くAndroid 33　にてクラッシュする原因だった。こちらの公式パッケージを代わりに使う`

com.google.play.core-1.8.2
`com.google.play.review-1.8.1 を使う時に必要`


# ❓ Issues
Android 33 にてクラッシュしてしまう
iOS14 でビルドできない


# 👏 Solve

解決方法を順番に説明する

## 1. 🫥 Easy mobile Pro の修正

EasyMobileDependencies.xml の 18~22 を Uncomment する。この`android.play:core`を入れているとAndroid 31~ でクラッシュしてしまう。

```xml:EasyMobileDependencies.xml 18~22行目
    <!-- <androidPackage spec="com.google.android.play:core:1.6.+">
      <repositories>
        <repository>https://maven.google.com</repository>
      </repositories>
    </androidPackage> -->
```

プロジェクト内に `android.core` が名前に含まれるファイルがある場合全部削除する。


:::note warn
注意
このファイルは`in-app-review` に関与している。削除するとEasy mobile pro を使った`in-app-review` は機能しない。
:::


## 2. 🌟 `in-app-review` 機能の追加

`com.google.play.review-1.8.1`
`com.google.play.core-1.8.2`
の2つのunitypackage を公式からダウンロード後、import する。

その後、Force resolve する。

独自の `in-app-review` スクリプトを作成する。

**定義側**

```cs: AppReview.cs
using System.Collections;
using UnityEngine;

public static class AppReview
{
    public static IEnumerator Request()
    {
#if UNITY_IOS
        UnityEngine.iOS.Device.RequestStoreReview();

#elif UNITY_ANDROID
        var reviewManager = new Google.Play.Review.ReviewManager();
        var requestFlowOperation = reviewManager.RequestReviewFlow();
        yield return requestFlowOperation;

        if (requestFlowOperation.Error != Google.Play.Review.ReviewErrorCode.NoError)
        {
            // エラー処理が必要な場合ここに追加
            Debug.LogError(requestFlowOperation.Error);
            yield break;
        }

        var playReviewInfo = requestFlowOperation.GetResult();
        var launchFlowOperation = reviewManager.LaunchReviewFlow(playReviewInfo);
        yield return launchFlowOperation;

        if (launchFlowOperation.Error != Google.Play.Review.ReviewErrorCode.NoError)
        {
            // エラー処理が必要な場合ここに追加
            Debug.LogError(launchFlowOperation.Error);
            yield break;
        }
#else
        Debug.Log("RequestReview Not supported.");
#endif
        yield break;
    }
}
```

**呼び出し側**

```cs: ReviewManager.cs
using UnityEngine;

public class ReviewManager : MonoBehaviour
{
    public void ReviewDialogDown()
    {
        // Show the rating dialog with default behavior
        StartCoroutine(AppReview.Request());
    }
}
```

## 3. 🎬 Admob package の修正

iOS SDK version 9.13.0 を使っているので、iOS 14 ビルドのためには以下の対応が必要。これをしないとビルドできない。

```diff_xml:GoogleMobileAdsDependencies.xml
  <iosPods>
-    <iosPod name="Google-Mobile-Ads-SDK" version="~> 9.11">
+    <iosPod name="Google-Mobile-Ads-SDK" version="9.13">
      <sources>
        <source>https://github.com/CocoaPods/Specs</source>
      </sources>
    </iosPod>
  </iosPods>
```

## 5. 👣 ATT 対応（App Tracking Transparency）

`GoogleMobileAds-v8~`以降は標準でATT対応される。ただ今回は、Easy mobile pro の関係で`v7.3.1`を使っているので、スクリプトで設計する必要がある。

**定義側**
⚠️ `Assets > Plugin > iOS` に配置する。


<details><summary>廃止 `RequestAttDialog.mm`</summary>

```objc: RequestAttDialog.mm
#import <Foundation/Foundation.h>
 #import <AppTrackingTransparency/AppTrackingTransparency.h>
 #import <AdSupport/AdSupport.h>
 
 //この範囲のコードはCで書かれてるよってこと
 #ifdef __cplusplus
 extern "C" {
 #endif
 //ATTダイアログを表示するメソッド
 void requestIDFA() {
     if (@available(iOS 14, *)){
         [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
         // Tracking authorization completed. Start loading ads here.
         // [self loadAd];
         }];
     }
 }
 #ifdef __cplusplus
 }
 #endif
```

</details>


🆕 

```swift:RequestIDFADialog.swift
import Foundation
import AppTrackingTransparency
import UserMessagingPlatform

extension UIViewController {
    static func getFrontViewController() -> UIViewController? {
        let keyWindow = UIApplication.shared.connectedScenes
            .filter({$0.activationState == .foregroundActive})
            .map({$0 as? UIWindowScene})
            .compactMap({$0})
            .first?.windows
            .filter({$0.isKeyWindow}).first

        let vc = keyWindow?.rootViewController
        guard let _vc = vc?.presentedViewController else {
            return vc
        }
        return _vc
    }
}

public class UmpPlugins {
    public static func requestIDFA() {
        if #available(iOS 14, *) {
            // UMPRequestParameters
            let parameters = UMPRequestParameters()
            // 許諾年齢未満であるか？
            parameters.tagForUnderAgeOfConsent = false
            
            // 事前許諾プロンプトの提示状況をリクエスト
            // メインスレッドで呼ぶ必要あり
            UMPConsentInformation.sharedInstance.requestConsentInfoUpdate(
                with: parameters,
                completionHandler: { error in
                    if let error = error {
                        // エラー
                        debugPrint(error.localizedDescription)
                    } else {
                        // 事前許諾プロンプトが利用可能かチェック
                        let formStatus = UMPConsentInformation.sharedInstance.formStatus
                        // UMPFormStatus.unknown：0 不明
                        // UMPFormStatus.available：1 利用可能
                        // UMPFormStatus.unavailable：2 利用不可
                        if formStatus == .available {
                            DispatchQueue.main.async {
                                // プロンプトをロード
                                UmpPlugins.loadIDFA()
                            }
                        }
                    }
                }
            )
        }
    }
    
    private static func loadIDFA() {
        
        // プロンプトの提示が必要
        guard let vc = UIViewController.getFrontViewController() else {
            return
        }
        UMPConsentForm.loadAndPresentIfRequired(from: vc)
    }
}

@_cdecl("requestIDFA")
public func requestIDFA()
{
    return UmpPlugins.requestIDFA()
}

```

**定義側 2️⃣**
⚠️ `Assets > Editor` に配置する。
```cs:AddElementsInfoplist.cs
#if UNITY_IOS
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using UnityEngine;
public class AddElementsInfoplist
{
    [PostProcessBuild]
    public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath)
    {
        // Info.plist に Privacy - Tacking Usage Description(NSUserTrackingUsageDescription)を追加する（ステップ２）
        string infoPlistPath = buildPath + "/Info.plist";
        PlistDocument infoPlist = new PlistDocument();
        infoPlist.ReadFromFile(infoPlistPath);
        PlistElementDict root = infoPlist.root;
        root.SetString("NSUserTrackingUsageDescription", "あなた様の好みに合わせた広告を表示するために使用されます！！！！");
        infoPlist.WriteToFile(infoPlistPath);
        // PBXProjectクラスというのを用いてAppTrackingTransparency.frameworkを追加していきます（ステップ３）
        string pbxProjectPath = PBXProject.GetPBXProjectPath(buildPath);
        PBXProject pbxProject = new PBXProject();
        pbxProject.ReadFromFile(pbxProjectPath);
        string targetGuid = pbxProject.GetUnityFrameworkTargetGuid();
        pbxProject.AddFrameworkToProject(targetGuid, "AppTrackingTransparency.framework", true);
        pbxProject.WriteToFile(pbxProjectPath);
    }
}
#endif
```

**定義側 3️⃣**
```cs:ShowAttDialog.cs
using System.Runtime.InteropServices;
public class ShowAttDialog
{
#if UNITY_IOS
    [DllImport("__Internal")]
    private static extern int requestIDFA();
    //public staticにしているのd外部ファイルで「ShowAttDialog.RequestIDFA()」とすれば呼び出せます
    public static void RequestIDFA()
    {
        requestIDFA();
    }
#endif
}
```


**呼び出し側**

```cs:AwakeManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using EasyMobile;
using UnityEngine.SceneManagement;

public class AwakeManager : MonoBehaviour
{
    private void Awake()
    {
#if UNITY_IOS
        ShowAttDialog.RequestIDFA();
#endif
    }
}
```

Package Manager の Localization に **NSUserTrackingUsageDescription** を設定しておく


## 6. 🔨 iOS build 対応

全て `ENABLE_BITCODE = NO` にする必要がある。以下のファイルを`Assets/Editor`内に設定する。

```cs:OnPostBuildProcess.cs
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using System.IO;
public class OnPostBuildProcess : MonoBehaviour
{
    [PostProcessBuild]
    public static void OnPostProcessBuild(BuildTarget buildTarget, string path)
    {
        if (buildTarget == BuildTarget.iOS)
        {
            processForiOS(path);
        }
    }
    static void processForiOS(string path)
    {
        string pbxPath = PBXProject.GetPBXProjectPath(path);
        PBXProject pbx = new PBXProject();
        pbx.ReadFromString(File.ReadAllText(pbxPath));
        string target = pbx.GetUnityMainTargetGuid();
        pbx.SetBuildProperty(target, "ENABLE_BITCODE", "NO");
        target = pbx.GetUnityFrameworkTargetGuid();
        pbx.SetBuildProperty(target, "ENABLE_BITCODE", "NO");
        File.WriteAllText(pbxPath, pbx.WriteToString());
    }
}
```

## 7. ⚙️ その他 Unity での設定項目

- [x] もちろん Target API Level を `Android 33` に設定
- [x] `package manager` Unity Ads SDKに関する項目を削除（使わないから）
- [x] `app icon`の修正、特に`adaptive icon`のデザイン
- [x] privacy policy URL をアプリ内に追記
- [x] `Package　Manager` > `Localization`を導入して l10n の実装
- [x] Twitter icon を 𝕏 icon に変更
- [x] Instagram URL -> TikTok URL に変更

できれば
- [ ] `in-app-purchase` 機能の追加


## 8. 🌐 Web上での対応
- [x] Admob　のメディエーション紐付け
- [x] データセーフティの申告

できれば
- [ ]  Admob の GDPR 画面を設定

# おしまい
おつです。