iOS/Androidアプリから複数のFirebaseプロジェクトを使用する

こんにちは。株式会社ビデオマーケットの仙台オフィスでMIRAILのiOS/Androidアプリ開発を担当しているasmzです。

今回はちょっとニッチなFirebaseの利用法をご紹介します。

はじめに

皆さんはFirebaseを利用したアプリ開発を行っている時、1つのアプリから複数のFirebaseプロジェクトを使いたくなることはありませんか?ありませんね、わかります。

例えば、開発環境用・本番用など環境毎に複数のFirebaseプロジェクトを用意し、iOS/Androidアプリのビルド時に GoogleService-Info.plistgoogle-services.json を読み替えることで、実際に参照するFirebaseプロジェクトを使い分ける、といったことは結構あるかと思います。

f:id:asmz0:20210129143354p:plain

ただ今回はそういう意味の「複数」ではなく、1つのビルドバイナリからFirebaseプロジェクトXとFirebaseプロジェクトY両方とも参照したい、といったケースです。

f:id:asmz0:20210129143427p:plain

普通にFirebaseを使っていれば上記のような状況になることはあまりないと思いますが、例えば以下のようにFirebaseプロジェクト自体作り直さないと対応できないような場合、既存のサービスを継続しつつもう一つFirebaseプロジェクトが欲しくなってきます。

  • Cloud Firestoreなどで使用するリソースロケーション(リージョン)を変えたい
  • Firebaseと統合したGCPプロジェクト側でDatastoreモードを使用しているため、Firebase側のCloud Firestoreが使用できない

このような状況の時、どう対応すれば良いのか今回まとめてみました。

そんなことできるの?

アプリ開発では多くの場合、Firebaseコンソールから出力されるGoogleService-Info.plistgoogle-services.jsonを自身のアプリプロジェクトに取り込むことでFirebaseを利用しているかと思います。

ただ、これらのファイルは1ファイル内で複数のproject_idを記載できるような形にはなっておらず、またファイル名も決まっていてビルド時に複数ファイルを取り込ませることも難しそうなので、そもそも1アプリから複数のFirebaseプロジェクトを扱える、というイメージがあまり沸かないのではないでしょうか(私がそうでした…)

実はこれ、以下の通り公式でサポートされています。

firebase.google.com

そのため、公式ドキュメントと内容が重複する部分は多いのですが、以降は細かい説明を加えながらiOS/Androidそれぞれの具体的な設定手順をご紹介します。

事前準備

前提として、アプリが既に何らか1つFirebaseプロジェクトを使用中で、そこに新たなFirebaseプロジェクトを使用したいとします。今回は便宜上これをプライマリ/セカンダリと呼びます。

まず、iOS/Android共にセカンダリのFirebaseプロジェクトへのアプリの追加が必要です。これは通常のFirebaseプロジェクトと同様にFirebaseコンソールから行ってください。(ここでは手順は割愛します)

アプリ追加後にダウンロードできるようになるGoogleService-Info.plistgoogle-services.jsonは以降の手順でちょっとだけ使うので、どこかに保存しておいてください。

iOSアプリ(Swift)設定手順

セカンダリプロジェクトの設定

以下のようにGoogleService-Info.plistのファイル内容を見ながら FirebaseOptions オブジェクトを作成し、そのオブジェクトを使ってセカンダリとなるFirebaseApp を用意します。

let secondaryOptions = FirebaseOptions(
    googleAppID: "1:12345678:ios:1234567812345678",
    gcmSenderID: "12345678901"
)
secondaryOptions.apiKey = "your_api_key"
secondaryOptions.projectID = "your_project_id"
// 以下は必須項目ではないが、利用するFirebase機能に応じて必要なものを設定
secondaryOptions.bundleID = "your.app.bundle.id"
secondaryOptions.trackingID = "UA-12345678-1"
secondaryOptions.clientID = "12345678901-abcdefg.apps.googleusercontent.com"
secondaryOptions.databaseURL = "https://yourproject.firebaseio.com"
secondaryOptions.storageBucket = "yourproject.appspot.com"
secondaryOptions.androidClientID = "12345.apps.googleusercontent.com"
secondaryOptions.deepLinkURLScheme = "yourapp://"
secondaryOptions.storageBucket = "your_project_id.appspot.com"
secondaryOptions.appGroupID = nil

// FirebaseAppへセカンダリ設定(nameは目的に合わせた任意の文字列)
FirebaseApp.configure(name: "secondary", options: secondaryOptions)

// セカンダリFirebaseApp取得(これを各Firebase機能で使用する)
guard let secondary = FirebaseApp.app(name: "secondary")
  else { assert(false, "Could not retrieve secondary app") }

セカンダリFirebaseの利用

上記で用意したセカンダリFirebaseAppインスタンスを用いることで、セカンダリFirebaseプロジェクトのFirebase各機能を利用することができるようになります。

// Cloud Firestore
let secondaryFirestore = Firestore.firestore(app: secondary)

// Real Time Database
let secondaryDatabase = Database.database(app: secondary)

// Functions
let secondaryFunctions = Functions.functions(app: secondary)

// Storage
let secondaryStorage = Storage.storage(app: secondary)

// Authentication
let secondaryAuth = Auth.auth(app: secondary)

// Remote Config
let secondaryConfig = RemoteConfig.remoteConfig(app: secondary)

// ML Vision
let secondaryVision = Vision.vision(app: secondary)

以降はプライマリと同じ形で利用が可能となります。

Androidアプリ(Kotlin)設定手順

セカンダリプロジェクトの設定

Androidの方もgoogle-services.jsonを見ながら、iOSと同じようなアプローチでセカンダリFirebaseAppを用意していきます。

val options = FirebaseOptions.Builder()
        .setProjectId("your_project_id")
        .setApplicationId("1:12345678:android:1234567812345678")
        .setApiKey("your_api_key")
        // 以下は必須項目ではないが、利用するFirebase機能に応じて必要なものを設定
        .setGcmSenderId("12345678901")
        .setGaTrackingId("UA-12345678-1")
        .setDatabaseUrl("https://yourproject.firebaseio.com")
        .setStorageBucket("yourproject.appspot.com")
        .build()

// FirebaseAppへセカンダリ設定(第1引数は Context、第3引数は目的に合わせた任意の文字列)
Firebase.initialize(this, options, "secondary")

// セカンダリFirebaseApp取得(これを各Firebase機能で使用する)
val secondary = Firebase.app("secondary")

セカンダリFirebaseの利用

各Firebase機能の利用方法は以下の通りです。

// Cloud Firestore
val secondaryFirestore = Firebase.firestore(secondary)

// Real Time Database
val secondaryDatabase = Firebase.database(secondary)

// Authentication
val secondaryAuth = Firebase.auth(secondary)

// Functions
val secondaryFunctions = Firebase.functions(secondary)

// Storage
val secondaryStorage = Firebase.storage(secondary)

// Remote Config
val secondaryConfig = Firebase.remoteConfig(secondary)

// ML Vision
val secondaryVision = FirebaseVision.getInstance(secondary)

// Dynamic Links
val secondaryLinks = Firebase.dynamicLinks(secondary)

留意事項

公式ドキュメントに記載がありますが、現状Analyticsは追加Firebaseプロジェクトには対応していません。(デフォルトのプロジェクトにのみログが記録される)

また、公式リファレンスを調べてみたところ、以下の機能についても追加Firebaseプロジェクトを指定するメソッドが用意されていないようなので、現状は対象外と思われます。

  • Crashlytics
  • Cloud Messaging
  • In-App Messaging
  • Dynamic Links(iOSのみメソッドが用意されておらず対象外。Androidにはメソッドある)

まとめ

以上がアプリから複数のFirebaseプロジェクトを利用する方法でした。

もともと1つのFirebaseプロジェクトで十分に要件をカバーできるのであれば、その方が良いかと思います。

ただ、既にサービス運用中で今になってプロジェクトの構成変更が効かない場合や、新しい機能追加にあたり新たなデータベースや認証管理が必要になった場合などに、今回のような形でプロジェクトを拡張することができる、という点は覚えておいて損はないかなと思います。