Edited at
DiverseDay 20

Poiboyで使用しているFirebaseの機能紹介と事例ついでにおまけ


はじめに

はい!こんにちは〜!

株式会社Diverseで今年からPoiboyのiOSエンジニアとして働かせてもらっているgiiiitaです!

アーキテクチャ(PoiboyはMVP,興味本位でMVVM)であったりAutoLayoutあったり覚えることがてんこ盛りですが、日々の新しいインプットが新鮮でエンジョイしてます!

昨日は、@yuki-oさんFlareをFlutterだけじゃなくてJSでもFlareった話でした。

試行錯誤した結果本家にprを出す行動・・・かっこいいなぁと!

Flutterを採用した大規模なプロダクトを開発されているチームの方なので、このプロダクトがリリースされた際にはぜひFlutterノウハウお聞きしたいですね!

本日は、

Diverse Advent Calendar 2018 20日目の記事として

Poiboyチームが今年から導入した技術の一つFirebaseについて記事にしたいと思います!

※この記事では触れてない使い方もあります


Firebaseとは

一言で言うとGoogleが提供しているmBaaS(mobile Backend as a Service)です。

サービス開発で頻繁に使われる認証とかPush通知等汎用的な機能を比較的簡単に実装が行えたりりFireStoreといったフルマネージドなDBがあるおかげでインフラ構築、サーバーの管理、スケールを行う必要もなく開発側はアプリケーション開発に集中することができ結果リリースまでの時間を短縮させてくれるサービスだと考えています。

また、

認証機能など開発中の助けとなる機能だけではなくログ収集、収益化、A/BTestなど運用フェーズの助けとなる機能も豊富なところも注目の一つかと思っております。

ある程度の制限はございますが、無料枠でFirebaseの機能を試すことができますので興味がある方はぜひTRYしてみてください!

詳しくは、

Firebase公式ドキュメント

Firebaseのgithub

上二つのリンクを見てみると良さそうです。


Poiboyで使用している機能

Poiboyチームでは今年よりFirebaseが提供するいくつかの機能を実際のプロダクトで使用しているのでその例を紹介します!


Authentication

主にFacebook,Twitter,SMSの3つのログイン経路を作成するために使用しております。

Poiboyチームで行ってる設定例

Poiboyのログイン画面↓↓↓

LINEログインなどはAuthenticationがカバーするログイン方法として提供されてはいないですがカスタム認証トークンを用いてLINEでログインが実現できるようです。

Authenticationを使用してTwitterログインを行う方法についてはこちら

Authenticationのガイドはこちら


Cloud Functions

エラー検知の仕組みとして使用しております。

Cloud FunctionではHTTPであったりDBにデータが入った時などいくつかのイベントをトリガーに設定し行いたい処理を書くことができます。

Poiチームでは、Analyticsのイベントをコードに仕込みそのイベントがAnalytics/Eventsに通知された際にSlackへ通知を飛ばす仕組みをとっています。

Analytics/Eventをトリガーとするfunctionを用意


sample.ts

export const faceBookLoginFailedNotificationToSlack = functions.analytics.event('FaceBookLoginFailed').onLog(async (event) => {

//analyticsEventに"FaceBookLoginFailed"が通知された時に実行したい処理内容を書く
});

Analytics/Eventを通知させたい箇所に以下コードを仕込む


sample.swift

Analytics.logEvent("FacebookLoginFailed", parameters: nil)


こんな具合でslackに通知が届く

今回は通知のためにCloud Functionsを使用しましたが、

他にもFireStoreでは出来ないこと(ドキュメントに含まれる2つのフィールドに対して囲検索が出来ない等)をCloud FunctionsとAlgoliaを使用して出来ないことをカバーする方法があります。

ドキュメントが追加(onCreate),更新(onUpdate)されることをトリガーにFireStoreのデータをAlgoliaに送りアプリから検索を行いたい際にはAlogilaからデータを引くイメージです。

公式でも紹介されているので気になった方はこちら

functionsをDeployするときのコマンド集はこちら

Cloud Functionsのガイドはこちら

functions-sample


RemoteConfig

ABTest,特定の値を即時切り替えたい箇所で使用しております。

Poiboyチームで行ってる設定例

具体的に、

・ABTestでは、ABそれぞれ異なるパターンのユーザ体験を用意し数パーセントのユーザにBパターンを反映されるように仕組みます。

その結果、意図した結果であれば徐々にBパターンが反映されるユーザの範囲を広げたりしております。

特定の値をすぐに切り替えたい箇所では、

とある画面の更新が終わるまでに30秒間隔を開けたい際にこの30をRemoteConfigで設定します。

設定例では30と設定されていますが、30ではなく20にしようとなった時もアプリの更新を行う必要がなく設定画面でパラメータを20にしてあげれば即時反映されます。

RemoteConfigの良さとしてリアルタイム性がありもし仮に不具合、不都合があった場合には管理画面で値を切り替えてしまえば良いのでリスク回避も柔軟に行えます。

既存コンテンツであれば、RemoteConfigの導入箇所は考えればいくつかあるかなと思っておりまして例えば、以下のような感じです。

・緊急メンテを行いたい場合管理画面で値を切り替え即時メンテ用の画面に切り替える

・ソシャゲのイベントでイベント完走率が見込んだ数字より低ければ難易度を下げる

etc...(考えればチームにフィットした使い方があるはず!!!)

アプリの更新をせずに専用のコードさえ仕込んでおけば管理画面からポチッと切り替えられるのは強い!!!

FirebaseでABTestを行う方法についてはこちら

RemoteConfigのガイドはこちら


Firebaseの機能を導入してみて

チームで決めた施策やテストを行う際にそれが反映、実現されるまでのスピード感があるなと感じてます。

ABTestを行うにしろ基盤作成から工数を見積もるといつテストが始まるのだろう?となりがちなところをFirebaseの機能にのっかってしまえば後はテストしたい箇所に数行コードを書くだけでテストが行えたりするので便利だと感じております。

認証とかはアプリではほぼ必須な機能ですが、Authenticationを使用することで少ない工数で実装が可能なのでアプリケーション開発に時間を回せるのもエンジニア的には嬉しいのではないでしょうか?


おまけ


ユーザ登録からユーザ画面までを実装してみる


リポジトリ用意しました

サンプルリポジトリ

だいたい1時間もかからないくらいでこの実装が行えたのでかなりコスパ良いかなと思ってます!


イメージ

Login画面に表示されているボタンをタップすることでLogin処理を行います。

Login処理が完了後にプロフィール用の画像、名前を入力してDBに保存します。

プロフィール用の画像、名前が無事DBに保存されればユーザ画面を表示します。


使用するFirebaseのサービス

Authentication <- 認証に匿名ログインを使用

FireStore <- UserDataのread/write

CloudStorage <- Userのプロフィール画像の置き場所


実装箇所でポイントだと思う箇所をピックアップ

サンプルリポジトリに含まれるコードでFirebaseを使って機能実装している箇所でポイントとなる箇所のみ抽出してみました!


ログイン済みかどうかで最初に表示するViewControllerを選択


AppDelegate.swift

if let userId: String = Auth.auth().currentUser?.uid {

Firebase.User.get(userId) { (user, error) in
if let error = error { return }
guard let user = user else { return }
let vc: UserViewController = UserViewController.instantiate(with: .init(user: user))
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()

}
} else {
let vc: LoginViewController = LoginViewController.instantiate()
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
}



UserModelの定義

Pringを使用することで、FireStoreのDocumentをオブジェクトのように扱うことが可能です。

ここでは、ユーザ登録情報として必須のname,thumbnail,必須項目が登録済みであるかのisActivatedを含むオブジェクトを作成しています。


Firebase+User.swift

import Foundation

import Firebase
import FirebaseFirestore
import Pring

extension Firebase {
@objcMembers
class User: Object {

dynamic var name: String?
dynamic var thumbnail: File?

// nameとthumbnailが登録されていれば利用可能
dynamic var isActivated: Bool = false

}
}



匿名ログイン


LoginViewController.swift

    private func registrationAnonymus() {

Auth.auth().signInAnonymously(completion: { (authDataResult, error) in
if let error = error { return }
guard let authDataResult = authDataResult else { return }
let id: String = authDataResult.user.uid
let user: Firebase.User = Firebase.User(id: id)
user.save { (ref, error) in
if let error = error { return }
print("Registration Success")
let vc: UserRegistrationViewController = UserRegistrationViewController.instantiate(with: .init(user: user))

let nvc: UINavigationController = UINavigationController(rootViewController: vc)

self.present(nvc, animated: true)

}
})
}


signInAnonymouslyWithCompletion: メソッドがエラーの場合は早期return,エラーがない場合は匿名ユーザーのアカウントデータをFIRAuthDataResultオブジェクトから取得しています。

user.save { (ref, error) in }でエラーがなければ新しくUserコレクション内に新規のドキュメントが生成されます。

Auth.auth().signInAnonymously(completion: { (authDataResult, error) in

if let error = error { return }
guard let authDataResult = authDataResult else { return }
let id: String = authDataResult.user.uid
let user: Firebase.User = Firebase.User(id: id)
user.save { (ref, error) in
if let error = error { return }
}
})


UserDocumentの更新

Userオブジェクトのnameに値を入れて

self.user.updateを行うとFireStoreのUserDocumentのFieldが更新されます。


UserRegistrationViewController.swift

        self.user.name = name

self.user.update { [weak self] (error) in
if let error = error { return }
}


Firebase管理画面のスクショ

Autentication周り

匿名ログインを有効にして、ユーザ作成が行われるとこのように表示されます。



Firestore

Firestore内のデータは以下スクショのように確認出来ます。

UserCollectionの中にそれぞれのUserDocumentが作成されています。

管理画面上で検索も可能です。

Cloud Storage

ユーザのthumbnailがCloud Storageに保存されております。

FirestoreのUserDocumentにはCloudStorageに保存されているthumbnailのDownLoadURLが格納されています。


明日のアドカレ内容について

Diverse Advent Calendar 2018 21日目は@mina_mさんデザイナーが考える!心理学を取り入れて少しでもマッチングの確率を上げる方法についてです!

マッチングの確率を上げる方法として心理な観点からどのようなアプローチがあるのか楽しみですね!