Unity

【Unity】iOSネイティブ連携でGoogle Sign Inを実装する方法

UnityでGoogle Sign Inを実装する場合、Google公式のライブラリを利用するのが一般的です。

しかし、最終更新日が5年ぐらい前で、最新のFirebaseライブラリーと組み合わせて使うと動きません。
また公式ライブラリは内部でWebViewを使っていますが、現在はWebViewの代わりにWKWebViewを使わないとAppleの審査が通らないので実質使えません。

この問題を解消するため、iOSネイティブ連携でGoogle Sign Inを行う方法と、Sign Inした結果をUnity用のFirebaseAuthライブラリーと連携させる方法を詳しく解説します。

 

Androidネイティブ連携でのGoogle Sign Inを行う方法はコチラ

【Unity】Androidネイティブ連携でGoogle Sign Inを実装する方法UnityでGoogle Sign Inを実装する場合、Google公式のライブラリを利用するのが一般的です。 しかし、最...

 

Firebaseの用意

Firebaseにアプリ用のプロジェクトを作成したらUnityアプリとして登録し、iOSの欄に必要項目を記入します。

そして、Unity側で呼ぶFirebaseAuthとiOSネイティブのGoogle Sign Inを紐付けるための準備をしていきます。

 

Google Sign Inの有効化

構築欄のAuthenticationを選び、新しいプロバイダを追加からGoogleを選び、Google Sign Inを有効にします。

設定ファイルの配置

全ての準備が完了したら、左上の歯車ボタンを押して、プロジェクトの設定を選び、googleServices-info.plistをダウンロードしたら、UnityプロジェクトのAssetsフォルダに配置します。

 

iOSネイティブでGoogle Sign In

では、iOSネイティブのGoogle Sign Inを実装していきましょう。
まず、File > Build Settingsで、PlatformをiOSに切り替えておきます。

Google Sign Inライブラリーの追加

コードを実装していく前に、エラーがでないようGoogle Sign Inライブラリーを最初に追加しておきます。

iOSフォルダ内にあるPodfileを開き、target ‘UnityFramework’ do から end の間に次の1行を追加します。

pod 'GoogleSignIn'

 

追加したら、ターミナルを開き、iOSフォルダ直下に移動して「pod update」コマンドを実行します。

 

MyUnityAppControllerの実装

最新のiOSアプリではちょっと違うのですが、ちょっと前まで、iOSネイティブはUIApplicationDelegateプロトコル(インタフェース)を実装したAppDelegateというクラスがアプリ起動の起点になっていました。

Unityの場合はUIApplicationDelegateプロトコルを実装した「UnityAppController.mm」がアプリの起動と同時に呼び出されます。
(iOS/Classesの中にあります)

このUnityAppControllerを継承したMyUnityAppControllerに起動クラスを差し替え、そこにGoogle Sign Inを実装することでUnityでのGoogle Sign Inを実現していきます。

という訳で早速実装していきましょう。

UnityでiOSビルドをすると作られるiOSワークスペースをXCodeで開いたら
File > New > File…
で「Cocoa Touch Class」を選び

Classに、MyUnityAppController
Subclass ofに、UnityAppController
Languageは、Objective-C
とします。

保存先は、Assets/Plugins/iOS
Targetsは、Unity-iPhoneを外し、UnityFrameworkに変えてください。
こっちにしないとコードヒントがでなくてコーディングしにくいです。

実装内容は以下の通り。

コードの詳細は全部コメントに書いたのでそちらを参照してください。
コードはObjective-Cで書いてください。Swiftで書く方法もありますがまどろっこしいので。

#import "Prefix.pch"
#import "MyUnityAppController.h"
#import "GoogleSignIn.h"

@implementation MyUnityAppController


+ (void)googleSignIn {
    UnityAppController *appDelegate = (UnityAppController *)[UIApplication sharedApplication].delegate;
    UIViewController *currentViewController = appDelegate.window.rootViewController;
    if (currentViewController) {
        [GIDSignIn.sharedInstance
         signInWithPresentingViewController:currentViewController
            completion:^(GIDSignInResult * _Nullable signInResult, NSError * _Nullable error)
         {
            if (error) {
            	// Unity側のTestController.GetIDTokenFailedメソッドを呼び出す。
            	// 第一引数はクラス名、第二引数は関数名、第三引数は関数の引数
                UnitySendMessage([@"TestController" UTF8String], [@"GetIDTokenFailed" UTF8String], [[NSString stringWithFormat:@"%ld", error.code] UTF8String]);

            } else {
                if (signInResult.user.idToken != nil) {
                	// Unity側のTestController.GetIDTokenメソッドを呼び出す。
            		// 第一引数はクラス名、第二引数は関数名、第三引数は関数の引数
                    UnitySendMessage([@"TestController" UTF8String], [@"GetIDToken" UTF8String], [signInResult.user.idToken.tokenString UTF8String]);

                } else {
                	// Unity側のTestController.GetIDTokenFailedメソッドを呼び出す。
            		// 第一引数はクラス名、第二引数は関数名、第三引数は関数の引数
                    UnitySendMessage([@"TestController" UTF8String], [@"GetIDTokenFailed" UTF8String], [@"" UTF8String]);
                }
            }
        }];
    } else {
        NSLog(@"ViewControllerを取得できませんでした。");
    }
}

#ifdef __cplusplus
extern "C" {
#endif

// Unity側から呼び出すメソッド
void googleSignIn() {
    [MyUnityAppController googleSignIn];
}

#ifdef __cplusplus
}
#endif

// GoogleSignInの結果をハンドリングする
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    
    BOOL superResult = [super application:app openURL:url options:options];
    
    BOOL handled = [GIDSignIn.sharedInstance handleURL:url];
    if (handled) {
        return YES;
    }
    
    return superResult;
}


@end

 

起動クラスの差し替え

デフォルトでは、UnityAppControllerが呼び出されるようになっているので、そこを先程作ったMyUnityAppControllerが呼び出されるよう、Classes/mainファイルを書き換えます。

AppControllerClassNameにセットされている文字列のところ。

#include "RegisterFeatures.h"
#include <csignal>
#include "UnityInterface.h"
#include "../UnityFramework/UnityFramework.h"

void UnityInitTrampoline();

// WARNING: this MUST be c decl (NSString ctor will be called after +load, so we cant really change its value)
// 差し替える
const char* AppControllerClassName = "MyUnityAppController";
//const char* AppControllerClassName = "UnityAppController";

 

info.plistの修正

次に、OAuth2でGoogle Sign InするためのクライアントID情報をInfo.plist (XCode上では拡張子無しのInfoと表示されています)に追加します。

XCodeでInfoファイルを右クリックしてOpen As > Source Code
でInfo.plistの内容が表示されたら、次の項目を追加します。

YOUR_IOS_CLIENT_IDの欄には、FirebaseからダウンロードしたGoogleService-Info.plist内にある「CLIENT_ID」の値を、YOUR_DOT_REVERSED_IOS_CLIENT_IDの欄には、GoogleService-Info.plist内にある「REVERSED_CLIENT_ID」の値を記入します。

<key>GIDClientID</key>
<string>YOUR_IOS_CLIENT_ID</string>
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>YOUR_DOT_REVERSED_IOS_CLIENT_ID</string>
    </array>
  </dict>
</array>

 

仕上げ (必ずやらないとダメ)

最後に、全ての実装が完了したら必ずMyUnitAppControllerのヘッダーと本体のTarget Membershipを外してください。

Assets/Plugins/iOSに配置したファイルはiOSビルドすると自動的にコピーがiOSプロジェクト内にマージされるため、Targetのチェックを入れたままだとファイルが2重になってduplicateエラーが起こります。

その後Unity側でiOSビルドを実行します。

iOSビルドを実行することで先程作ったAssets/Plugins/iOS/MyUnityAppControllerファイルがiOSプロジェクト内にコピーされて組み込まれるので、必ず忘れないようにしてください。

以上でiOSネイティブ側の実装は完了です。

試しに実機でiOSビルドを試して見てください。

問題なく起動するはずです。

 

 

Unity用FirebaseAuthと連携

次に、Unity側にFirebaseAuthパッケージを導入して、iOSネイティブで成功したGoogle Sign Inの結果をFirebase Authに紐付ける処理を実装していきます。

FirebaseAuthパッケージの導入

以下のリンクから最新版のFirebase Unity SDKをダウンロードします。
今回は、2023/7/20時点で最新のバージョン11.2.0を導入します。

既に他のFirebaseパッケージを導入済の場合は同じバージョンのFirebaseAuthパッケージを入れるか、最新のパッケージを全て入れ直してください。
そうしないとエラーで動きません。

ダウンロードしたら解凍して、FirebaseAuth.unitypackage をimportします。

C#の実装

iOSネイティブのMyUnityAppControllerで、UnityのTestControllerのメソッドを呼び出すよう実装していたので、クラス名はTestControllerとします。

TestControllerに定義したSignInWithGoogleメソッドをButtonなどに当てて呼び出すと、iOS側のGoogle Sign Inが実行され、結果がUnity側のメソッドを通して返ってきます。

詳細はコードのコメントを参照してください。

using UnityEngine;
using Firebase.Auth;
using Firebase.Extensions;
using System.Runtime.InteropServices;

public class TestController : MonoBehaviour
{
    private FirebaseAuth auth;
    Firebase.DependencyStatus dependencyStatus = Firebase.DependencyStatus.UnavailableOther;

    private void Start()
    {
        // FirebaseAuthの初期化
        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
        {
            dependencyStatus = task.Result;
            if (dependencyStatus == Firebase.DependencyStatus.Available)
            {
                auth = FirebaseAuth.DefaultInstance;
            }
            else
            {
                Debug.Log($"error: {dependencyStatus}");
            }
        });
    }

    [DllImport("__Internal", EntryPoint = "googleSignIn")]
    static extern void googleSignIn();

    // ネイティブに定義してあるメソッドを呼ぶ
    public void SignInWithGoogle()
    {
#if UNITY_IOS
        // iOSネイティブのメソッドを呼ぶ
        googleSignIn();

#elif UNITY_ANDROID
        // Androidネイティブのログインはこっちに
#else
        Debug.Log($"SignInWithGoogle error: unknown platform");
#endif
    }

    // ネイティブから呼び出される。Google Sign Inで取得したIDTokenを受け取る
    public void GetIDToken(string idToken)
    {
        Credential credencial = GoogleAuthProvider.GetCredential(idToken, null);       
        // GoogleアカウントをAuthに紐付ける
        auth.SignInWithCredentialAsync(credencial).ContinueWith(authTask =>
        {
            if (authTask.IsCanceled)
            {
                Debug.Log($"LinkWithCredentialAsync Cancelled");
            }
            else if (authTask.IsFaulted)
            {
                Debug.Log($"LinkWithCredentialAsync Faulted");
            }
            else
            {
                Debug.Log($"LinkWithCredentialAsync Complete!");
            }
        });
    }

    // ネイティブから呼び出される。Google Sign Inに失敗した事を教えてもらう
    public void GetIDTokenFailed(string errorCode)
    {
        Debug.Log($"GetIDToken Failed!! {errorCode}");
    }
}

 

うまく処理が完了すると、FirebaseAuthとの連携が完了し、FirebaseのAuthenticationにGoogle Sign Inしたユーザーの情報が登録されます。