-------------------------------------------
2017/8/26追記
Firebase for Unity 4.1.0を使用することで、特別な設定をすることなくUnity Cloud Buildが使用できるようになりました。以下の記事はFirebase for Unity1.1の情報です。

2017/9/11追記
"-lz}"のリンカエラーが発生するようになったので、結局、同様の手順で手動で入れました。Unity Cloud Buildでエラーになった際の解決法 vol3もとても参考になります。
-------------------------------------------

Firebase Analytics + Push Notificationにおける、ローカル環境でのLinker Errorの解消方法とUnity Cloud Buildでの使用方法をまとめました。

Firebase for Unityのインポート

FirebaseのUnity PackageはDownload the SDKからダウンロードすることができます。今回は、Push Notificationを実装するため、FirebaseAnalyticsとFirebaseMessagingの2つのUnity Packageをプロジェクトにインポートします。

firebase_unity


いきなり本番のプロジェクトに導入するのはリスクがあるので、サンプルプログラムをダウンロードして、そこにUnity Packageをインポートするのがオススメです。

iOSでPush通知を使用するには、APNs の SSL 証明書のプロビジョニングが必要です。AppleのProvision Portalで取得した証明書をp12に変換し、Firebaseのサイトに登録します。

その後、Firebaseのサイトから、プロジェクトのgoogle-services.jsonとGoogleService-Info.plistをダウンロードし、UnityのAssetsの中の好きなフォルダに登録します。

これで、Push通知が使用できるようになります。

ローカル環境でのLinker Error

iOSビルドでLinker Errorが出る場合は、cocoapodsのバージョンを確認します。コマンドラインからpod --versionとすると、バージョンが表示されますが、これが1.0以降でない場合に、Firebaseがうまくリンクされません。

Unityは、Xcodeのプロジェクトファイルを生成する際、Podfileが存在する場合にpodコマンドを叩いてくれます。Firebaseはpodコマンドを使って依存ライブラリをダウンロードするのですが、podコマンドに失敗してエラーが発生した場合もプロジェクトファイルが生成されてしまうため、ビルドエラーが発生するようです。

podのバージョンが古い場合は、gem update cocoapodsでバージョンアップが可能です。

Unity Cloud BuildでのLinker Error

ローカルでビルドするにはこれだけでOKですが、Unity Cloud Buildではcocoapodsが使えないため、以下のエラーが出ます。

Error running cocoapods. Please ensure you have at least version 1.0.0.  You can install cocoapods with the Ruby gem package manager:

この場合、CocoaPods を使用せずに統合するからzipをダウンロードし、手動でFrameworksフォルダのFirebaseMessaging.frameworkなどをPlugin/iOSフォルダに置く必要があります。

copy


コピーする必要のあるファイルです。

## Analytics
  - FirebaseAnalytics.framework
  - FirebaseInstanceID.framework
  - GoogleInterchangeUtilities.framework
  - GoogleSymbolUtilities.framework
  - GoogleUtilities.framework
## AdMob (~> Analytics)
  - GoogleMobileAds.framework
## AppIndexing (~> Analytics)
  - FirebaseAppIndexing.framework
## Auth (~> Analytics)
  - FirebaseAuth.framework
  - GoogleNetworkingUtilities.framework
  - GoogleParsingUtilities.framework
## Crash (~> Analytics)
  - FirebaseCrash.framework
## Database (~> Analytics)
  - FirebaseDatabase.framework
## DynamicLinks (~> Analytics)
  - FirebaseDynamicLinks.framework
## Invites (~> Analytics)
  - FirebaseDynamicLinks.framework
  - FirebaseInvites.framework
  - GoogleAppUtilities.framework
  - GoogleAuthUtilities.framework
  - GoogleNetworkingUtilities.framework
  - GoogleParsingUtilities.framework
  - GooglePlusUtilities.framework
  - GoogleSignIn.framework

  You'll also need to add the resources in the
  Resources directory into your target's main
  bundle.
## Messaging (~> Analytics)
  - FirebaseMessaging.framework
  - GoogleIPhoneUtilities.framework
## RemoteConfig (~> Analytics)
  - FirebaseRemoteConfig.framework
  - GoogleIPhoneUtilities.framework
## Storage (~> Analytics)
  - FirebaseStorage.framework
  - GoogleNetworkingUtilities.framework

その上で、Firebase/Editor/AppDeps.csなど*Deps.csから以下のようにpodの呼び出しをコメントアウトします。

#elif UNITY_IOS
/*
        Type iosResolver = Google.VersionHandler.FindClass(
            "Google.IOSResolver", "Google.IOSResolver");
        if (iosResolver == null) {
            return;
        }
        Google.VersionHandler.InvokeStaticMethod(iosResolver, "AddPod", new object[] { "Firebase/Core" }, new Dictionary() { { "version", "3.10+" }, { "minTargetSdk", "7.0" } });
*/
#endif
また、Firebaseはsqlite3とAddressBook.frameworkを要求するのでEditorフォルダに以下のスクリプトを追加し、自動化します。-ObjCがないと実行時に例外が飛ぶので、これも追加します。
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
#if UNITY_IOS
using UnityEditor.iOS.Xcode;
using System.Collections.Generic;
#endif
using System.IO;

public class PostBuildProcess {

    [PostProcessBuild]
    public static void OnPostProcessBuild (BuildTarget buildTarget, string path) {
#if UNITY_IOS
        string projPath = Path.Combine (path, "Unity-iPhone.xcodeproj/project.pbxproj");

        PBXProject proj = new PBXProject ();
        proj.ReadFromString (File.ReadAllText (projPath));

        string target = proj.TargetGuidByName ("Unity-iPhone");
        proj.AddBuildProperty (target, "OTHER_LDFLAGS", "-lz");
        proj.AddBuildProperty (target, "OTHER_LDFLAGS", "-lsqlite3");
        proj.AddBuildProperty (target, "OTHER_LDFLAGS", "-ObjC");

        List frameworks = new List () {
            "CoreData.framework",
            "AddressBook.framework"
        };

        foreach (var framework in frameworks) {
            proj.AddFrameworkToProject (target, framework, false);
        }

        File.WriteAllText (projPath, proj.WriteToString ());
#endif
    }
}

この段階で、Firebase Analyticsは動作するのですが、Firebase Messagingは実行時に例外で落ちます。どうやら、Unity Pluginと公式サイトのzipのFrameworkのバージョンが異なることが原因のようです。そのため、Firebase Messagingを使用する場合は、ローカルでcocoapodsを使用してiOSビルドした後、プロジェクトのFrameworksフォルダに含まれるFirebaseのFrameworkで、zipからコピーしたFrameworkを置き換える必要があります。

copy


また、GTMDefines.h、GTMLogger.h、GTMLogger.m、GTMNSData+zlib.h、GTMNSData+zlib.mもPlugins/iOSにコピーした上で、InspectorでCompile Flagsに-fno-objc-arc -fobjc-exceptionsと記載する必要があります。

compile_flag


Androidでは、UnityでAndroid Buildに変更して、Assets -> Play Service Resolver -> Android Resolver -> Resolve Client Jarsを実行しておきます。

これでようやく、UnityでFirebase AnalyticsとNotificationを使用することができます。

firebase


XcodeのCapabilityにPush NotificationとBackground Modes -> Remote notificationを追加すると、Firebaseから発行した通知を受け取ることができます。ただし、Info.plistにFirebaseAppDelegateProxyEnabled=NOを設定しないと、アプリがバックグラウンドに回った後、フォアグラウンドに回ると、100%ハングします。

IMG_1873


XcodeのCapability設定とFirebaseAppDelegateProxyEnabledの設定をUnity Cloud Buildで自動化するには、Unityから自動でPush NotificationsをONにしたいを参考に、Editorスクリプトを作成する必要があります。

//Unity Cloud BuildでiOS Buildにfirebaseの依存ファイルを追加する
//加えて、CapabilityにPush Notificationを追加する

using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
#if UNITY_IOS
using UnityEditor.iOS.Xcode;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
#endif

public class PostBuildProcess {

    [PostProcessBuild]
    public static void OnPostProcessBuild (BuildTarget buildTarget, string path) {
#if UNITY_IOS
        string projPath = Path.Combine (path, "Unity-iPhone.xcodeproj/project.pbxproj");

        PBXProject proj = new PBXProject ();
        proj.ReadFromString (File.ReadAllText (projPath));

        string target = proj.TargetGuidByName ("Unity-iPhone");
        proj.AddBuildProperty (target, "OTHER_LDFLAGS", "-lz");
        proj.AddBuildProperty (target, "OTHER_LDFLAGS", "-ObjC");
        proj.AddBuildProperty (target, "OTHER_LDFLAGS", "-lc++");
        proj.AddBuildProperty (target, "OTHER_LDFLAGS", "-lsqlite3");

        List frameworks = new List () {
            "CoreData.framework",
            "AddressBook.framework"
        };

        foreach (var framework in frameworks) {
            proj.AddFrameworkToProject (target, framework, false);
        }

        //Add
        File.WriteAllText (projPath, proj.WriteToString ());

        //Set Push Notification Capability
        CreateEntitlements(path,YourAppName);
        SetCapabilities(path);
        SetBackgroundMode(path);
#endif
    }

#if UNITY_IOS
    private static void SetBackgroundMode(string path)
    {
        var plistPath = Path.Combine(path, "Info.plist");

        PlistDocument plist = new PlistDocument();
        plist.ReadFromFile(plistPath);

        plist.root.SetBoolean("FirebaseAppDelegateProxyEnabled",false);

        PlistElementArray bgModes = plist.root.CreateArray("UIBackgroundModes");
        bgModes.AddString("remote-notification");

        plist.WriteToFile(plistPath);
    }

    private static void CreateEntitlements(string path,string your_appname)
    {
        XmlDocument document = new XmlDocument ();
        XmlDocumentType doctype = document.CreateDocumentType ("plist", "-//Apple//DTD PLIST 1.0//EN", "http://www.apple.com/DTDs/PropertyList-1.0.dtd", null);
        document.AppendChild (doctype);

        XmlElement plist = document.CreateElement ("plist");
        plist.SetAttribute ("version", "1.0");
        XmlElement dict = document.CreateElement ("dict");
        plist.AppendChild (dict);
        document.AppendChild (plist);

        XmlElement e = (XmlElement) document.SelectSingleNode ("/plist/dict");

        XmlElement key = document.CreateElement ("key");
        key.InnerText = "aps-environment";
        e.AppendChild (key);

        XmlElement value = document.CreateElement ("string");
        value.InnerText = "development";
        e.AppendChild (value);

        string entitlementsPath = path + "/Unity-iPhone/"+your_appname+".entitlements";
        document.Save(entitlementsPath);

        string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
        PBXProject proj = new PBXProject ();
        proj.ReadFromString (File.ReadAllText (projPath));
        string target = proj.TargetGuidByName ("Unity-iPhone");
        string guid = proj.AddFile (entitlementsPath, entitlementsPath);
        proj.SetBuildProperty(target, "CODE_SIGN_ENTITLEMENTS", "Unity-iPhone/"+your_appname+".entitlements");
        proj.AddFileToBuild(target, guid);
        proj.WriteToFile(projPath);
    }

    private static void SetCapabilities(string path)
    {
        string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
        PBXProject proj = new PBXProject ();
        proj.ReadFromString (File.ReadAllText (projPath));

        string[] lines = proj.WriteToString().Split ('\n');
        List newLines = new List ();
        bool editFinish = false;

        for (int i = 0; i < lines.Length; i++) {

            string line = lines [i];

            if (editFinish) {
                newLines.Add (line);
            } else if (line.IndexOf ("isa = PBXProject;") > -1) {
                do {
                    newLines.Add (line);
                    line = lines [++i];
                } while (line.IndexOf("TargetAttributes = {") == -1);

                // 以下の内容はxcodeprojの内容にあるproject.pbxprojを参照してください
                newLines.Add("TargetAttributes = {");
                newLines.Add("xxxxxxxx = {");
                newLines.Add("DevelopmentTeam = xxxxxxxx;");
                newLines.Add("SystemCapabilities = {");
                newLines.Add("com.apple.BackgroundModes = {");
                newLines.Add("enabled = 1;");
                newLines.Add("};");
                newLines.Add("com.apple.Push = {");
                newLines.Add("enabled = 1;");
                newLines.Add("};");
                newLines.Add("};");
                newLines.Add("};");
                editFinish = true;

            } else {
                newLines.Add (line);
            }
        }

        File.WriteAllText(projPath, string.Join ("\n", newLines.ToArray ()));
    }
#endif
}

FirebaseAppDelegateProxyEnabled=NOに設定したので、個別ユーザへのPushが必要な場合は、didRegisterForRemoteNotificationsWithDeviceTokenでFIRInstanceID.instanceID().setAPNSTokenを呼ぶ必要があります。iOSでプッシュ通知を実装する方法の超詳細まとめ(前編)の図のように、APNから取得したDeviceTokenをFirebaseに送らないと、個別ユーザへのPush通知ができないためです。Unity Cloud Buildで自動化したいので、UnityのiOSでAppDelegateに処理を追加するを参考に、iOSのPluginを書くのが順当ですが、ハングする問題は修正中とのことなので、とりあえずUnity SDK 1.2を待ってもよいかもしれません。(2017/1/19追記 Unity SDK 1.1.1がリリースされ、この問題は修正されました。SDK 1.1.1を使用する場合、上記、PostBuildScriptにUserNotifications.frameworkを追加し、plist.root.SetBoolean("FirebaseAppDelegateProxyEnabled",false)をコメントアウトします)

わりと大変なので、Unity Cloud Buildのcocoapods対応か、Firebase Pluginのzip版を期待したいです。