Google Firebase A/Bテストをiosで使ってみる

A/Bテストやってますか？Qiitaの```ABテスト```タグの量を見るかぎりあまり使われていないのかな？

GoogleのFirebaseにはA/Bテストを簡単に行える機能があります。これをiosアプリに実装する方法を書きたいと思います。以下の記事を参考にしました。

https://www.raywenderlich.com/17323848-firebase-remote-config-tutorial-for-ios

https://www.raywenderlich.com/20974552-firebase-tutorial-ios-a-b-testing

今回は画面に表示されたボタンのテキストをAとBで変えてクリックされる率をとるというものを作りたいと思います。
![21.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/dea100fa-548b-f3eb-a316-6c2f4393512f.jpeg)

Firebaseにはいろいろな機能があるのですが、Remote ConfigとAnalyticsという機能を使うことでA/Bテストを実装します。

Remote Configは字のごとく設定ファイルをサーバから取得するものです。その設定ファイルに含まれている ```button_title = "クリック"``` のようなデータをアプリ側でボタンのテキストに設定してあげます。

そしてボタンがクリックされたときにAnalyticsを使ってイベントをサーバに投げることでクリック率の測定が行われます。

実装の大まかな流れはこうなります。

+ Firebaseにプロジェクトを作成
+ iosアプリを新規作成しFirebaseの設定をする
+ FirebaseにA/Bテストを追加（Remote Configが追加される）
+ iosでRemote Configを取得しボタンの表示を分ける
+ ボタンが押されたらAnalyticsでイベントを呼ぶ

# Firebaseにプロジェクトを作成

[Firebase console](https://console.firebase.google.com) にアクセスしプロジェクトを作成します。
![01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/ce0a9e0d-a344-449b-86ed-6f08c9fe78e4.jpeg)

プロジェクト名は```abtest```として次に進みます。
![02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/fdaa4dbd-dff6-1699-812d-49eb75e91ec8.jpeg)

とくに変更することなく次へ進みます。
![03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/b24b12c7-b4df-758b-180f-4c3f655f83c4.jpeg)

Googleアナリティクスアカウントを選択もしくは作成して次に進みます。
![04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/42b9e3e6-851a-3100-d9fa-396c9216018e.jpeg)

作成が完了するとプロジェクトのページが表示されます。
![05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/424bd0a7-8475-4e13-7203-3da55629f54c.jpeg)

# Firebaseにiosアプリを登録

次にiosアプリをFirebaseに登録します。

![06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/1b8dd0bf-8345-116d-0a44-c409ba32d9f6.jpeg)

iosアプリのBundle Idenfitierを設定し次へ進みます。他のところは空欄でかまいません。

![07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/af990238-5217-2107-cb23-ae754f0a8b40.jpeg)


```GoogleService-Info.plist``` をダウンロードしてiosアプリのプロジェクトに取り込みます。
![08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/7d679e06-91b3-dee6-7d7e-1c6f6838dcc2.jpeg)

![10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/500643e6-6ed9-a2d5-3531-7faa1c0618e7.jpeg)


iosアプリは ```Bundle Idenfitier``` を ```com.akira.abtest``` とし、最初のビューにボタンを配置し、 ```ViewController.swift``` からボタンへのアクセスとクリックされたときの関数を追加しておきます。


![09_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/faf4a6ad-076d-e9db-d5e4-123df3395f0b.jpeg)

```swift:VewController.swift
class ViewController: UIViewController {
    @IBOutlet weak var button: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func onButton(_ sender: Any) {
    }
}
```

![11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/46fd8256-17f8-b955-d24f-5bb97c71ba6c.jpeg)


```pod``` でアプリにFirebaseのライブラリを追加します。

```sh
$ pod init
``` 

```Podfile``` に ```Firebase/Core``` ```Firebase/RemoteConfig``` ```Firebase/Analytics``` を追加します。

```:Podfile
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'abtest' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for abtest
  pod 'Firebase/Core'
  pod 'Firebase/RemoteConfig'
  pod 'Firebase/Analytics'

  target 'abtestTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'abtestUITests' do
    # Pods for testing
  end

end
```

ライブラリをインストールして　xcode を ```abtest.xcworkspace``` で開き直します。

```sh
$ pod install
```

Firebaseの初期化をする必要があるのですが、これは後ほど説明します。
![12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/38acff0e-c20f-e8c8-ae23-553e48aa6e46.jpeg)

コンソールに戻ります。
![13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/aa58227f-7949-8c1f-00e6-123a0e4a4b83.jpeg)

# A/Bテストの登録

アプリを登録するとA/Bテストを作れるようになっています。
![14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/ac4f765e-44ba-eea7-5450-28586b6ffd7a.jpeg)

Remote Configを作成します。
![15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/8b4bc862-3c1c-5873-73c9-760029f9e30d.jpeg)

テスト名を ```button_click``` として次へ進みます。
![16.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/086d6c6e-606e-f350-48c6-7e0fb297e5d4.jpeg)

登録したアプリを選択し100%の利用者でテストを有効にします。ユーザー数が多いサービスの場合は全体の30%などでテストを行うのもよいでしょう。
![17.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/c7e2a026-44db-5117-5924-4de9fd9f2645.jpeg)

次に目標を設定します。ここではボタンがクリックされたときに呼ぶイベントを作成します。 ```button_click_event``` とします。定着率などを計測したい場合はそちらの項目を選択してください。
![18.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/ef5433db-bbb4-0173-786c-fd8f15470b6e.jpeg)

次にバリアントを設定します。バリアントとはつまりAとかBです。３パターンならCもあるでしょう。ここでは　```button_title``` というパラーメータに ```クリック``` と ```🍖クリック🍖```　を設定します。これをアプリのボタンに設定して表示を分けます。
![19_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/fc3a9efb-5bc0-cafe-d51e-05e9e2c0ebd2.jpeg)

テストを開始します。
![20.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/c8c64fab-19d7-6e5a-9379-2fea84c5c2bc.jpeg)

# アプリでボタンのテキストを分ける

次はアプリにFirebase A/Bテストを入れていきます。まずはFirebase Remote Configを使うクラスを作ります。

```swift:RCValues.swift
import Firebase

class RCValues {
    enum ValueKey: String {
        case button_title
    }

    static let sharedInstance = RCValues()

    private init() {
        loadDefaultValues()
        fetchCloudValues()
    }

    func loadDefaultValues() {
        let appDefaults: [String: Any?] = [
            ValueKey.button_title.rawValue : "クリック",
        ]
        RemoteConfig.remoteConfig().setDefaults(appDefaults as? [String: NSObject])
    }

    func activateDebugMode() {
        let settings = RemoteConfigSettings()
        settings.minimumFetchInterval = 0
        RemoteConfig.remoteConfig().configSettings = settings
    }

    func fetchCloudValues() {
        activateDebugMode()
        RemoteConfig.remoteConfig().fetch { [weak self] _, error in
            if let error = error {
                print("Uh-oh. Got an error fetching remote values \(error)")
                return
            }
            RemoteConfig.remoteConfig().activate { _, _ in
            }
        }
    }

    func string(forKey key: ValueKey) -> String {
        return RemoteConfig.remoteConfig()[key.rawValue].stringValue!
    }
}
```

適当な場所に置きxcodeに取り込んでください。

```RCValues``` はシングルトンで ```RCValues.sharedInstance``` でアクセスします。インスタンスが作成されると ```loadDefaultValues()``` で初期値を設定し```fetchCloudValues()``` でFirebaseからRemote Configを取得してきます。 

```activateDebugMode()``` はデバック用のコードです。 ```minimumFetchInterval = 0``` にすることでインタンス作成時に必ずRemote Configを取得してきます。未設定の場合は１日おきなど、ある程度時間が立たないと更新されません。

次にFirebaseとRCValuesを初期化します。

```swift:AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        FirebaseApp.configure()
        _ = RCValues.sharedInstance
        return true
    }

```

```FirebaseApp.configure()``` でFirebaseを初期化し、 ```_ = RCValues.sharedInstance``` でRCValuesのインスタンスを作成します。

最後にRemote Configの値を使ってボタンのテキストを変更し、ボタンがタップされたらAnalyticsでイベントを呼び出します。

```swift:ViewController.swift
import UIKit
import Firebase

class ViewController: UIViewController {
    @IBOutlet weak var button: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let buttonTitle = RCValues.sharedInstance.string(forKey: .button_title)
        button.setTitle(buttonTitle, for: .normal)
    }

    @IBAction func onButton(_ sender: Any) {
        Analytics.logEvent("button_click_event", parameters: nil)
    }
}
```
```RCValues.sharedInstance.string(forKey: .button_title)``` でRemote Configで設定したテキストを取ってきてボタンに設定できます。

そしてボタンをタップされたときに ```Analytics.logEvent("button_click_event", parameters: nil)``` でイベントを送信します。これでどちらのボタンの方がクリック率が高いのかが測定できます。

これでアプリを何回か実行してみてください。半々の確率でテキストが変わると思います。

![21.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/24e52033-32dd-763c-17ff-610ad1d424c4.jpeg)

# 特定のデバイスにAとBを指定する

開発時にはランダムで表示されるよりもどちらかに固定したい場合があると思います。その場合はデバイスを登録して指定できます。

```swift:AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        FirebaseApp.configure()
        Installations.installations().authToken { result, _ in
            print("Your instance ID token is \(result?.authToken ?? "n/a")")
        }
        _ = RCValues.sharedInstance
        return true
    }

```

```FirebaseApp.configure()``` の直後に ```Installations.installations().authToken``` を出力してその値をコピーしてください。

次にFirebase Consoleでデバイスを登録します。

![22.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/21de1c7b-cb84-b08e-bcc7-23b05e1497c1.jpeg)

テストデバイスを管理を選択し
![23.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/b2fb40a4-9527-bf27-176b-396888350aa4.jpeg)

先程コピーしたトークンを貼り付け、バリアントを選択し、追加してから保存します。
![24.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/24cbb53a-e912-c845-57d2-24c195a890fa.jpeg)

これでどちらのテキストを表示するのか決めることができます。


# おしまい