Firebase A/B Testing を使って A/Bテストを気軽に実装する

先日、Firebase RemoteConfig に新機能「A/B Testing」が追加されました（[Firebase の A/B テスト改善のお知らせ](https://developers-jp.googleblog.com/2017/12/announcing-better-ab-testing-with.html)）。

まだベータ版の機能ではありますが、A/Bテストがとても簡単にできるようになっているようなので、使ってみたいと思います。

なお、本記事の動作環境は以下のとおりです[^1]。

* Firebase (4.7.0)
* FirebaseRemoteConfig (2.1.0)
* XCode 9.2
* Swift 3

## Firebase Remote Config の導入
Firebaseコンソールでプロジェクトを作成し、plistを配置したら、SDKをCocoaPodsでインストールします。
A/Bテストを行うには、Firebase Core および Remote Config の最新版が必要です[^2]。

```
pod 'Firebase/Core'
pod 'Firebase/RemoteConfig'
```

## A/B テストの準備 (Firebase コンソール側設定)
RemoteConfigの設定画面に「A/Bテスト」というボタンが増えています。

![RemoteConfigトップ](https://qiita-image-store.s3.amazonaws.com/0/43540/9f4af7d3-7402-4a68-db6e-5d83a0c0f3d2.jpeg)

現状、このボタンが唯一のA/Bテスト画面への動線です。クリックして早速テストを作成しましょう。

### 基本設定
まずは、テストの名前、説明、対象となるターゲットユーザー、およびテストを実施するユーザーの割合を選択します。

![基本設定](https://qiita-image-store.s3.amazonaws.com/0/43540/5df42ed9-ada5-10e6-09a2-c127973e3f76.jpeg)

名前や説明はコンソール上で使うので、わかりやすいものが良いでしょう。

ターゲットは、どのアプリにテストを実施するかを選択しますが、それ以外にも、Firebase Analyticsで設定されているユーザー層、ユーザープロパティ、地域や言語などによってテストするユーザーを絞り込むことが可能です。

Predictionsによるセグメントも指定可能なので、例えば７日以内に離脱しそうなユーザーに対してのみABテストを行う、といったことも可能です。

ユーザーの割合は、このセグメント内で何％にテストを行うかを設定します。この後A/Bテストのパターンを設定しますが、各パターンに対する割合は均等割り付けになる（50%/50%, 33%/33%/33%など)ので注意してください[^3]。

### バリアント（パターン設定）

![バリアント設定](https://qiita-image-store.s3.amazonaws.com/0/43540/ea01be8e-a29b-48e7-3a60-0b24d1c587cf.jpeg)

ここで、RemoteConfigのキーと値を設定します。ここでは、`test_sample_key`というキーの変数として、Aパターン（コントロールグループ=従来ユーザー）に`green`を、Bパターンに`blue`を指定しました。また、Bパターンのグループ名にもblueを指定しています。

### 目標
最後に目標（A/Bテストの目的や成功・失敗を判断するための基準）を設定します。

![スクリーンショット-2017-12-22-17.19.51.jpg](https://qiita-image-store.s3.amazonaws.com/0/43540/92920443-bbff-dc32-aa10-1c65770cbd86.jpeg)

ユーザーエンゲージメント、定着、購入による収益といった組み込みの目標のほか、「Analyticsの特定のイベント発生を最大化する」という目標を設定することも可能です。

![管理画面](https://qiita-image-store.s3.amazonaws.com/0/43540/ec757ea4-0849-1b81-06dd-687aa8669671.jpeg)

以上でテストが作成できました。作成されたテストは「下書き」に入ります。

## 実装

つづいて、アプリ側の実装です。
まずはAppDelegateで初期化します。

```Swift:AppDelegate.swift
import Firebase
import FirebaseInstanceID

// 以下 application(_ didFinishLaunchingWithOptions:)

// Use Firebase library to configure APIs
FirebaseApp.configure()
debugPrint(InstanceID.instanceID().token())

let remoteConfig = RemoteConfig.remoteConfig()
remoteConfig.setDefaults(fromPlist: "RemoteConfig-default")
if let settings = RemoteConfigSettings.init(developerModeEnabled: true) {
    remoteConfig.configSettings = settings
}
```

`InstanceID.instanceID().token()` は後ほどデバッグで使用します[^4]。

```xml:RemoteConfig-default.plist
<plist version="1.0">
<dict>
	<key>test_sample_key</key>
	<string>green</string>
</dict>
</plist>
```

デフォルト値を、plistやDictionaryで指定しておきます。このあたりはRemoteConfigの使い方通りなので割愛します。

つづいて、設定値を使う側の設定です[^5]。

```Swift:HogeViewController
remoteConfig.fetch(withExpirationDuration: TimeInterval(0), completionHandler: { (status, error) -> Void in
    if status == RemoteConfigFetchStatus.success {
        print("fetch success")
        self.remoteConfig.activateFetched()
    } else {
        print("fetch failed")
    }
    self.displayButtonColor()
})
```

`withExpirationDuration`で、どのくらいの時間、設定値をローカルにキャッシュするか決定します。今回はデバッグ用に0を指定していますが、実際はもっと長い時間にします。

```Swift
if let colorValue = remoteConfig["test_sample_key"].stringValue {
    if colorValue == "blue" {
        self.purchaseButton?.backgroundColor = UIColor.blue
        return
    }
}

self.purchaseButton?.backgroundColor = UIColor.green
```

ちょっとコードが雑ですが、`test_sample_key`の値を使ってビューを出し分けます。
キーの管理が大変なので、このあたりはenumにしたり、デフォルト設定を含めて共通クラス化しておきたいところです。

## 実装をテストする

A/Bテストだと面倒な実装のテストですが、Firebase A/B Testingなら簡単にテストを行うことが可能です。

まず、上記のコードをビルド・実行し、

```
debugPrint(InstanceID.instanceID().token())
```

で出力された値を控えておきます。

次に、Firebaseのコンソールからテストの画面を表示し、下書きの項目を展開します。

![下書き](https://qiita-image-store.s3.amazonaws.com/0/43540/dcddea0c-8f87-6b9b-dda1-64aeea98187d.jpeg)

「テスト端末の管理」をクリックします。

![テスト端末設定](https://qiita-image-store.s3.amazonaws.com/0/43540/82e8d9ae-3638-e511-e399-93e6291b5eb3.jpeg)

インスタンスIDトークンに先程控えたトークンを、バリアントに設定したいパターンを入力し、追加します。

この状態でRemoteConfigから値を取得すると、登録した端末については必ずこのパターンが返ってくるようになります。なお、この機能は下書き状態のテストのみで実行できますのでご注意ください。

## A/Bテスト開始
実装とそのテストが終わったら、いよいよA/Bテストを開始します。

![テスト中の画面](https://qiita-image-store.s3.amazonaws.com/0/43540/5ceca7b7-9566-da8b-c50d-36846ab2b784.jpeg)

有意差が出ると画面に表示されるほか、途中経過も確認が可能です。
また、対象ユーザーが少なくてテストが進まない場合は、「配信数を増加」からテストを実施するユーザーの割合を変更することが可能です。テストを終了する場合は、その隣のメニューから行います。

## 最後に

独自で基盤を作ろうと思うとかなり大変なA/Bテストですが、この新機能を使うととても簡単に、かつ強力なテストを行うことができそうです。

施策の効果測定がなかなかうまくいかない方は、ぜひお試しください。

## 参考

* [Create Firebase Remote Config Experiments with A/B Testing  |  Firebase](https://firebase.google.com/docs/remote-config/abtest-config?hl=ja)
* [Firebase を iOS プロジェクトに追加する  \|  Firebase](https://firebase.google.com/docs/ios/setup?hl=ja#available_pods)
* [Firebase Remote Config でモバイルアプリの A/B テストを実施する ｜ Developers\.IO](https://dev.classmethod.jp/smartphone/firebase-remote-config/)

[^1]: 執筆時点でベータ版の機能であり、本記事の内容は今後変更となることがありますのでご注意ください。

[^2]: なお、一部のドキュメントに「早期アクセス プログラム（EAP）ポッドの取得が必要」等の記載が残っていましたが、[公式ドキュメント](https://firebase.google.com/docs/remote-config/abtest-config?hl=ja)には「Firebase SDK v4.5.0以上が必要」としか書かれていませんので、通常通りのインストールで大丈夫そうでした。

[^3]: たとえば「全ユーザーの5%にBパターンを設定したい」ような場合は、アプリ以外のターゲットを指定せず、割合として「10%」と指定します（ただし、A/Bテストの結果はこの10%のユーザーに関する結果となりますので注意してください）。条件に当てはまるユーザーを半々に分けて大規模なテストをしたい場合は、セグメントを設定した上でここに「100%」を指定します。

[^4]: そういえばこれを書いていたとき、`FIRInstanceID`が呼び出せなくて、なんでだろうと思っていたら、`FirebaseInstanceID`をimportした上で`InstanceID`から呼び出す感じになっていました。他のクラスも軒並み`FIR`の接頭辞が消えていたのですが、これ消えたのいつでしたっけ……？

[^5]: 起動画面やチュートリアルでA/Bテストをしたい場合は、[AppRootController](https://speakerdeck.com/yimajo/approotcontrollerfalsegoti-an-jian-lue-shuo-ming-ban)みたいな、起動前の空のViewControllerで取得を行うようになるんだと思います。