Slide 1

Slide 1 text

Firebase Remote Configの運⽤で 知ったこと・知っておくと良いこと potatotips #62 @合同会社DMM

Slide 2

Slide 2 text

今泉 智博 (@imaizume) Androidユーザー歴8年のiOS開発者 株式会社Diverse : Poiboy iOS開発 株式会社UZUMAKI : 開発/SNS運⽤ ⾃⼰紹介

Slide 3

Slide 3 text

Firebase Remote Config概要 • アプリ内ロジックを遠隔で切り替える • 対象はOSバージョンやパーセンテージ等で指定 • Poiboyでは主にA/Bテスト時の振り分けに使⽤ • 導⼊から半年で10回以上A/Bテストを実⾏

Slide 4

Slide 4 text

参考: Remote Configの導⼊⽅法 (Swift 4) * application(didFinishLaunchingWithOptions)内に以下を追加 pod 'Firebase/Analytics' pod 'Firebase/RemoteConfig' Podfile FirebaseApp.configure() AppDelegate内 * Podfileに追加 * 取得したいフラグにデフォルト値を設定してから値を取得 let remoteConfig = RemoteConfig.remoteConfig() let parameter = “sample_beta_feature" /// デフォルト値は取得不能時等に使われる remoteConfig.setDefaults([parameter: false as NSObject]) /// 必要な型にキャストする let isBeta = remoteConfig[parameter].boolValue if isBeta { /* betaユーザーに対する処理 */ } 使いたい場所

Slide 5

Slide 5 text

今⽇のTopics • enumでのパラメータ管理 • パラメータのスタブ化 • その他運⽤⾯の話など Remote Configを実装・運⽤した時に気づいたTIPS

Slide 6

Slide 6 text

enumでのパラメータ管理

Slide 7

Slide 7 text

背景: パラメータを管理したい • パラメータはどんどん増える • A/Bテストなどの施策がたくさん⾛る • パラメータ管理はStringだときついのでenumに • デフォルトで利⽤可能なパラメータ型 • String / NSNumber / Data / Bool

Slide 8

Slide 8 text

パラメータを表すenumを定義 enum BoolParameter { case beta_feature_A case beta_feature_B … var defaultValue: Bool { switch self { case .beta_feature_A: return false … } } } enum StringParameter { … } enum NumberParameter { … } パラメータ型ごとのenum 型の種類に応じて 定義が必要 パラメータ名とデフォルト値を定義

Slide 9

Slide 9 text

パラメータ値の取得処理 FirebaseStore { func boolValue(for key: BoolParameter) -> Bool { let remoteConfig = RemoteConfig.remoteConfig() remoteConfig.setDefaults(key.defaultValue) return remoteConfig[key.rawValue].boolValue } func stringValue(for key: StringParameter) -> String? { let remoteConfig = RemoteConfig.remoteConfig() remoteConfig.setDefaults(key.defaultValue) return remoteConfig[key.rawValue].stringValue } } let store = FirebaseStore() let boolValue: Bool = store.boolValue(for: .beta_test_A) let stringValue: String? = store.stringValue(for: .beta_test_B) パラメータ値の取得処理 補完やfind usageの機能も利⽤可能に 取得⽤メソッドも 型ごとに⽤意

Slide 10

Slide 10 text

(アプリ側)パラメータ管理⽤OSS: Lobster • 管理周りの⾃前実装が不要に • 扱えるデータ型が拡張される (CollectionなどもOK!) https://github.com/sgr-ksmt/Lobster extension ConfigKeys { static let text = ConfigKey("text") } let text: String = Lobster.shared[.text] Lobsterでの実装 使った⼈いたらぜひ話を聞きたいです❗

Slide 11

Slide 11 text

(コンソール)パラメータ管理⽤OSS: remocon • YAMLにパラメータを定義 • コマンドで更新 • コンソールを触らないので 安全に管理できそう • エンジニアだけが触る前提 じゃないと難しい? https://github.com/jmatsu/remocon こちらも使った⼈いたら話を聞きたいです❗

Slide 12

Slide 12 text

パラメータのスタブ化

Slide 13

Slide 13 text

背景: 依存注⼊したい • テスト等でA/Bを任意の値に指定したい • そこでパラメータをスタブできるようにしておく • リポジトリパターンと抽象への依存を活⽤ (詳しくは過去に発表した資料もご覧ください) https://speakerdeck.com/imaizume/how-you-should-start-to-write-your-first-unit-test-for-ios

Slide 14

Slide 14 text

補⾜: 環境ごとに設定を分ける⽅法 let options = FirebaseOptions( contentsOfFile: FirebaseConfig.resourceFilePath())! FirebaseApp.configure(options: options) AppDelegate内 struct FirebaseConfig { static func resourceFilePath() -> String { #if DEBUG return Bundle.main.path(forResource: "GoogleService-development-Info", ofType: ".plist")! #else return Bundle.main.path(forResource: "GoogleService-Info", ofType: ".plist")! #endif } } Config 参考: https://qiita.com/giiiita/items/be71c14a59f596bc4001 QAやデバッグ⽤のパラメータを設定可能なので便利

Slide 15

Slide 15 text

抽象への依存を可能にするprotocolを定義 /// Bool型のパラメータを返すStoreに要求されるインターフェイス protocol BoolStoreProtocol { func boolValue(for key: BoolParameter) -> Bool } /// String?型のパラメータを返すStoreに要求されるインターフェイス protocol StringStoreProtocol { func stringValue(for key: StringParameter) -> String? } StoreProtocol.swift Presenter StoreProtocol Store Stub Firebaseから 取得した値 任意の指定値 true protocolで抽象化

Slide 16

Slide 16 text

Remote Configの値を返すStore class FirebaseStore {} extension FirebaseStore: BoolStoreProtocol { func value(for key: BoolParameter) -> Bool { let remoteConfig = RemoteConfig.remoteConfig() remoteConfig.setDefaults(key.defaultValue) return remoteConfig[key.name].boolValue } } extension FirebaseStore: StringStoreProtocol { func value(for key: StringParameter) -> String? { let remoteConfig = RemoteConfig.remoteConfig() remoteConfig.setDefaults(key.defaultValue) return remoteConfig[key.name].stringValue } } FirebaseStore.swift Storeは型:class = n:1で定義

Slide 17

Slide 17 text

任意の値を返すStub // 任意のパラメータで任意の値を返すことも可能 /// 常にfalseを返すスタブ class BoolStoreStub: BoolStoreProtocol { func boolValue(for key: BoolParameter) -> Bool { return false } } /// 常に"You are a beta user!"を返すスタブ class StringStoreStub: StringStoreProtocol { func stringValue(for key: StringParameter) -> String { return "You are a beta user!" } } StoreStub.swift Stubは型:class = 1:1で定義

Slide 18

Slide 18 text

呼び出し側 let boolStore: BoolStoreProtocol = FirebaseStore() let realBoolValue = boolStore.value(for: .beta_test_A) // true or false let boolStub: BoolStoreProtocol = BoolStoreStub() let stubBoolValue = boolStub.value(for: .beta_test_A) // always true class SomePresenter { private let firebase: BoolStoreProtocol init(_ firebase: BoolStoreProtocol = FirebaseStore()) { self.firebase = firebase } … } let realPresenter: SomePresenter = .init() let stubPresenter: SomePresenter = .init(boolStub) FirebaseRemoteConfigParameter.swift 抽象への依存でStubとStoreを区別なく使⽤可能に

Slide 19

Slide 19 text

その他運⽤⾯の話など

Slide 20

Slide 20 text

QAの⽅のための実装 • QA時にRemote Configの状態が知りたくなる • デバッグメニューに現在の値を表⽰してあげる QA担当者が分かりやすい表記で書こう✌

Slide 21

Slide 21 text

A/Bテストのやり⽅ • Poiboyでは… • ロジック切り替え: Firebase Remote Config • ログ送信: Treasure Data / Repro 等 • Firebase A/B Testing • 2019年6⽉現在beta版 • ⚠予めユーザープロパティを送る必要あり TreasureData.sendEvent( eventType: .someEvent, … isBetaUser: firebase.value(forKey: .beta_user_A) ) TreasureDataへのイベント送信 ロジック切り替えとイベント収集は別ツールでもOK✂

Slide 22

Slide 22 text

運⽤ルールの明⽂化 / ワークフローへの組み込み ルール・ワークフローの例 • パラメータ作成時 • 対応が分かるようフラグ名と条件名は同じに • 説明欄に意図や施策を⼈間の⾔葉で書く • A/Bテスト終わってもパラメータが放置される問題 • 毎スプリントで不要パラメータ消去タスクを積む • 事故防⽌のためQA項⽬に追加 • AndroidとiOSでパラメータ分ける時 • Androidは”a_”プレフィックスをつける 他社さんの運⽤周りのノウハウもぜひ知りたいです

Slide 23

Slide 23 text

まとめ ✓ パラメータはenumで管理 ✓ 複雑な管理はライブラリを使おう ✓ 開発⽤と本番⽤でプロジェクトを分けよう ✓ スタブ化するとテスト時に便利 ✓ 現在のパラメータをどこかに表⽰しよう ✓ フラグ管理とログ集計のツールは別でもOK ✓ 運⽤フローは早めに明⽂化 懇親会でもぜひ話しましょう

Slide 24

Slide 24 text

参考リンク • Poiboy(ポイボーイ) 出会い探しマッチングアプリ https://poiboy.jp/ • Firebase Remote Config (公式) https://firebase.google.com/docs/remote-config?hl=ja • Firebase Remote Config を使ってA/Bテストをやってみよう - PSYENCE:MEDIA https://tech.recruit-mp.co.jp/mobile/post-14940/ • sgr-ksmt/Lobster - Type-safe Firebase-RemoteConfig helper library https://github.com/sgr-ksmt/Lobster • jmatsu/remocon - a CLI for managing Firebase Remote Config based on YAML files. https://github.com/jmatsu/remocon • iOSアプリのテストを書きたいのに書けないあなたへ https://speakerdeck.com/imaizume/how-you-should-start-to-write-your-first-unit- test-for-ios • 環境ごとにFirebaseプロジェクトを使い分ける⽅法 (@giiiita) https://qiita.com/giiiita/items/be71c14a59f596bc4001