[Xamarin.Forms] Firebase Analyticsを導入してみたので、手順をまとめた! (Android & iOS)

2019年10月31日に「AndroidとiOS向けのGoogle Analytics」のサポートが終了します。

というわけで、移行する前準備として、XamarinでFirebase Analyticsを試してみました。

  • Q:なぜXamarin?
  • A:趣味です!

Firebase Analyticsのイベントについては、下記をどうぞ!

目次

環境

  • Windows
    • Windows 10 Pro
    • Visual Studio Community 2019 (Ver16.1.5)
  • macOS
    • macOS Mojave 10.14.5
    • Visutal Studio Community 2019 for Mac (Ver8.1.3)
    • Xcode 10.2.1
  • Android
    • Pixel 3a (Android 9)
  • iOS Simulator
    • iPhone Xs 12.2
  • Xamarin
    • Xamarin Ver16.1.0.545
    • Xamarin.Android SDK Ver9.3.0.23
    • Xamarin.iOS and Xamarin.Mac SDK Ver12.10.0.157
  • Library
    • Xamarin.Forms Ver4.0.0.425677
    • Xamarin.Firebase.Analytics Ver60.1142.1
    • Xamarin.Firebase.iOS.Analytics Ver5.5.0

アプリの新規作成

Visual Studioを起動し、「モバイルアプリ(Xamarin.Forms)」を選択します。

Visual StudioでXamarin.Formsを選択する

プロジェクト名を適当に入力します。

場所はできるだけ浅い階層にしましょう!(Windowsの場合)

「ソリューションとプロジェクトを同じディレクトリに配置する」にもチェックを入れておきます。

※Windwosでフォルダ/ファイル階層が深いと、Androidのビルドに失敗するためです。

プロジェクトの詳細を決める

テンプレートは何でも良いですが、シンプルな「空白」を選択しておきます。

テンプレートを選択する

Firebaseプロジェクトの作成

Firebase Consoleにアクセスし、「プロジェクトの作成」を選択します。

Firebaseプロジェクトを作成する

プロジェクト名を適当に入力し、アナリティクスの地域を日本にします。

Firebaseプロジェクトの詳細を決める

Androidアプリ

FirebaseプロジェクトにAndroidアプリを追加

Firebaseプロジェクトで、Androidマークを選択します。

FirebaseプロジェクトにAndroidアプリを追加

Androidアプリのパッケージ名をコピペ入力し、「アプリを登録」を選択します。

Androidアプリの登録

google-services.jsonをダウンロードし、「次へ」を選択します。

設定ファイルのダウンロード

Firebase SDKの追加は無視して進み、インストールを確認する画面を表示しておきます。

接続確認中

AndroidアプリのFirebase対応

NuGetパッケージのXamarin.Firebase.AnalyticsをAndroidプロジェクトにインストールします。

Androidプロジェクトにライブラリを追加

Visual StudioのAndroidプロジェクト直下に、ダウンロードしたgoogle-services.jsonを配置します。

設定ファイルを追加

続いて、google-services.jsonのビルドアクションをGoogleServicesJsonにします。 この項目が存在しない場合は、Visual Studioを再起動したり、ソリューションのリビルドをすれば現れます。

ビルドアクションを変更

MainActivity.csに初期化とイベント送信をお試し実装します。 ハイライト部分が追加したコードです。

using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

using Firebase.Analytics;

namespace FASample.Droid
{
    [Activity(Label = "FASample", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            var analytics = FirebaseAnalytics.GetInstance(this);

            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App());

            var bundle = new Bundle();
            bundle.PutString(FirebaseAnalytics.Param.ItemCategory, "Monday");
            bundle.PutString(FirebaseAnalytics.Param.ItemName, "21:54");
            analytics.LogEvent(FirebaseAnalytics.Event.SelectContent, bundle);
        }
        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

動作確認(Android)

それでは、アプリを実行しましょう!!

うまくいけば、10~60秒後ぐらいに「正常に追加されました」と表示されます!

接続成功

Dashboardでも1ユーザと認識されています。

リアルタイムの画面

DebugViewの設定(Android)

こちらを参考に実施します。

数分待つと、DebugViewにいろいろと表示されます!!

DebugViewの様子

出ない場合は、アプリを再起動させればOKでした。

iOSアプリ

FirebaseプロジェクトにAndroidアプリを追加

Firebaseプロジェクトで、「アプリを追加」からiOSマークを選択します。

Firebaseプロジェクトにアプリを追加

iOSアプリを選択

iOSアプリのバンドルIDをコピペ入力し、「アプリを登録」を選択します。 (実機で動作確認する場合は、実機インストール可能なバンドルIDを使用してください)

iOSアプリの情報を入力

GoogleService-Info.plistをダウンロードし、「次へ」を選択します。

設定ファイルをダウンロード

Firebase SDKの追加は無視して進み、インストールを確認する画面を表示しておきます。

接続待機中

iOSアプリのFirebase対応

NuGetパッケージのXamarin.Firebase.iOS.AnalyticsをiOSプロジェクトにインストールします。

iOSプロジェクトにライブラリを追加

Visual StudioのiOSプロジェクト直下に、ダウンロードしたGoogleService-Info.plistを配置します。

iOSプロジェクトに設定ファイルを追加

続いて、GoogleService-Info.plistのビルドアクションをBundleResourceにします。

ビルドアクションを変更

GoogleService-Info.plistIS_ANALYTICS_ENABLEDTrueに変更します。

<key>IS_ANALYTICS_ENABLED</key>
<true></true>

AppDelegate.csに初期化とイベント送信をお試し実装します。 ハイライト部分が追加したコードです。

using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using UIKit;

using Firebase.Analytics;

namespace FASample.iOS
{
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            // https://github.com/xamarin/GoogleApisForiOSComponents/issues/158#issuecomment-483194061
            var foo = Firebase.Core.Configuration.SharedInstance;

            Firebase.Core.App.Configure();

            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());

            var events = new Dictionary<string, string>
            {
                {
                    ParameterNamesConstants.ItemCategory,
                    "Friday"
                },
                {
                    ParameterNamesConstants.ItemName,
                    "17:08"
                },
            };

            var sendParams = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(
                events.Values.ToArray(),
                events.Keys.ToArray()
            );

            Analytics.LogEvent(EventNamesConstants.SelectContent, sendParams);

            return base.FinishedLaunching(app, options);
        }
    }
}

なお、ビルドした際に下記エラーが発生したため、var foo = Firebase.Core.Configuration.SharedInstance;を追加してます。

error MT5210: Native linking failed, undefined symbol: ...
error MT5211: Native linking failed, undefined Objective-C class: ...
error MT5201: Native linking failed. Please review the build log and the user flags provided to gcc: -ObjC -ObjC -lc++ -lsqlite3 -lz -ObjC -lc++ -lsqlite3 -lz
error MT5202: Native linking failed. Please review the build log.

動作確認(iOS)

それでは、アプリを実行しましょう!!

うまくいけば、10~60秒後ぐらいに「正常に追加されました」と表示されます!

接続成功

Dashboardでも1ユーザと認識されています。

リアルタイム画面の様子

DebugViewの設定(iOS)

こちらこちらを参考に--argument=-FIRDebugEnabledを追加します。

DebugViewの設定(iOS)

数分待つと、DebugViewにいろいろと表示されます!!

DebugViewの様子(iOS)

出ない場合は、アプリを再起動させればOKでした。

共通プロジェクトからイベント送信してみる(Xamarin.Forms)

せっかくなので、Xamarin.FormsのDependencyServiceを利用し、共通プロジェクトから呼び出しましょう。

参考にどうぞ!(どちらかというと、これが本命だったりします)

インターフェースを定義

共通プロジェクト(FASample)にIAnalytics.csを作成します。

using System.Collections.Generic;

namespace FASample
{
    public interface IAnalytics
    {
        void LogEvent(string eventName, Dictionary<string, object> eventParams);
        void Screen(string screenName);
    }
}

Androidの実装

Androidプロジェクト(FASample.Android)にAnalyticsSingleton.csAnalytics_Android.csを作成します。

using Android.App;
using Firebase.Analytics;

namespace FASample.Droid
{
    public class AnalyticsSingleton
    {
        public static AnalyticsSingleton GetInstance { get; } = new AnalyticsSingleton();

        public FirebaseAnalytics Analytics { get; set; }
        public Activity Activity { get; set; }

        private AnalyticsSingleton()
        {
        }
    }
}
using System.Collections.Generic;
using Android.OS;
using Xamarin.Forms;
using FASample.Droid;

[assembly: Dependency(typeof(Analytics_Android))]
namespace FASample.Droid
{
    public class Analytics_Android : IAnalytics
    {
        public void LogEvent(string eventName, Dictionary<string, object> eventParams)
        {
            if (eventName == null || eventParams == null) return;

            var analytics = AnalyticsSingleton.GetInstance.Analytics;

            var bundle = new Bundle();

            foreach (var eventParam in eventParams)
            {
                if (eventParam.Value.GetType() == typeof(string))
                {
                    bundle.PutString(eventParam.Key, (string)eventParam.Value);
                }

                if (eventParam.Value.GetType() == typeof(int))
                {
                    bundle.PutInt(eventParam.Key, (int)eventParam.Value);
                }
            }

            analytics.LogEvent(eventName, bundle);
        }

        public void Screen(string screenName)
        {
            if (screenName == null) return;

            var analytics = AnalyticsSingleton.GetInstance.Analytics;
            var activity = AnalyticsSingleton.GetInstance.Activity;

            analytics.SetCurrentScreen(activity, screenName, null);
        }
    }
}

続いて、MainActivity.csOnCreate()を次のようにし、FirebaseAnalyticsのインスタンスを保存します。

protected override void OnCreate(Bundle savedInstanceState)
{
    TabLayoutResource = Resource.Layout.Tabbar;
    ToolbarResource = Resource.Layout.Toolbar;

    AnalyticsSingleton.GetInstance.Analytics = FirebaseAnalytics.GetInstance(this);
    AnalyticsSingleton.GetInstance.Activity = this;

    base.OnCreate(savedInstanceState);

    Xamarin.Essentials.Platform.Init(this, savedInstanceState);
    global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
    LoadApplication(new App());
}

iOSの実装

iOSプロジェクト(FASample.iOS)にAnalytics_iOS.csを作成します。

using System.Collections.Generic;
using System.Linq;
using Foundation;
using Firebase.Analytics;
using Xamarin.Forms;
using FASample.iOS;

[assembly: Dependency(typeof(Analytics_iOS))]
namespace FASample.iOS
{
    public class Analytics_iOS : IAnalytics
    {
        public void LogEvent(string eventName, Dictionary<string, object> eventParams)
        {
            if (eventName == null || eventParams == null) return;

            var sendParams = NSDictionary<NSString, NSObject>.FromObjectsAndKeys(
                eventParams.Values.ToArray(),
                eventParams.Keys.ToArray()
            );

            Analytics.LogEvent(eventName, sendParams);
        }

        public void Screen(string screenName)
        {
            Analytics.SetScreenNameAndClass(screenName, null);
        }
    }
}

呼び出し側の実装

画面表示時に画面情報を送信し、ボタンを押したときにイベントを送信してみます。

MainPage.xamlMainPage.xaml.csを編集します。

 title="MainPage.xaml"]
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="FASample.MainPage">

    <StackLayout VerticalOptions="Center">
        <Label Text="Welcome to Xamarin.Forms!" 
               HorizontalOptions="Center"
               VerticalOptions="Center"/>
        <Button Text="send event"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                Clicked="Button_OnClicked" />
    </StackLayout>

</ContentPage>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Xamarin.Forms;

namespace FASample
{
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        protected override void OnAppearing()
        {
            DependencyService.Get<IAnalytics>().Screen("This is MainPage!!!");

            base.OnAppearing();
        }

        private void Button_OnClicked(object sender, EventArgs e)
        {
            var events = new Dictionary<string, object>
            {
                {
                    "item_category",
                    "August"
                },
                {
                    "item_name",
                    "18th"
                },
                {
                    "item_id",
                    18
                }
            };

            DependencyService.Get<IAnalytics>().LogEvent("select_content", events);
        }
    }
}

いざ、実行します!!

動作確認(Android)

screen_viewが飛んできました。

screen_viewが来た(Android)

詳細を見ると、This is MainPage!!!が表示されています!!

画面名が表示されている(Android)

続いて、アプリ画面のボタンを押すと、select_contentが飛んできました。

select_contentが来た(Android)

詳細を確認すると、バッチリ送信できています!!

イベントパラメータが設定されている(Android)

動作確認(iOS)

iOSも同様です!!

screen_viewが来た(iOS)

画面名が表示されている(iOS)

select_contentが来た(iOS)

イベントパラメータが設定されている(iOS)

さいごに

結果的には、意外と簡単にできました。(ハマりまくりましたけど……)

やり方は分かったので、あとは本番アプリに適用するだけです。(これが大変だけど……)

参考