LoginSignup
0
0

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

Last updated at Posted at 2023-08-30

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

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

🏕️ 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~ でクラッシュしてしまう。

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

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

注意
このファイルは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 スクリプトを作成する。

定義側

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;
    }
}

呼び出し側

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 ビルドのためには以下の対応が必要。これをしないとビルドできない。

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 に配置する。

廃止 `RequestAttDialog.mm`
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

🆕

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 に配置する。

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)を追加する(ステップ2)
        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を追加していきます(ステップ3)
        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️⃣

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
}

呼び出し側

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内に設定する。

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 での設定項目

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

できれば

  • in-app-purchase 機能の追加

8. 🌐 Web上での対応

  • Admob のメディエーション紐付け
  • データセーフティの申告

できれば

  • Admob の GDPR 画面を設定

おしまい

おつです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0