Firebase と Jetpack Compose を使用して Android アプリを作成する

1. はじめに

最終更新日: 2022 年 11 月 16 日

Firebase と Jetpack Compose を使用して Android アプリを作成する

この Codelab では、Make It So という Android アプリを作成します。このアプリの UI は、ネイティブ UI を作成するための Android の最新のツールキットである Jetpack Compose で完全に構築されています。直感的で、.xml ファイルを作成してアクティビティ、フラグメント、ビューにバインドするよりも、コードが少なくて済みます。

Firebase と Jetpack Compose の連携を理解するには、まず最新の Android アーキテクチャについて理解する必要があります。優れたアーキテクチャとは、コンポーネントの構成や相互通信が明確に示されるため、システムの理解、開発、メンテナンスが容易になります。Android の世界では、モデル - ビュー - ViewModel と呼ばれるアーキテクチャが推奨されます。モデルは、アプリケーションのデータにアクセスするレイヤを表します。View は UI レイヤであり、ビジネス ロジックについては何も認識しないようにする必要があります。また、ViewModel ではビジネス ロジックが適用されます。これには、ViewModelモデルレイヤを呼び出すことが必要になる場合があります。

こちらの記事を読んで、Jetpack Compose で構築された Android アプリに Model - View - ViewModel がどのように適用されるかを理解することを強くおすすめします。これにより、コードベースが理解しやすくなり、次のステップを完了しやすくなります。

作成するアプリの概要

Make It So は、タスクの追加と編集、フラグ、優先度、期限の追加、タスクの完了マーク付けができる、シンプルな ToDo リスト アプリケーションです。以下の画像は、このアプリケーションの 2 つのメインページを示しています。タスク作成ページと、作成されたタスクのリストが表示されているメインページです。

[Make it So Add Task] 画面 ホーム画面にカスタマイズ

このアプリにはない機能をいくつか追加します。

  • メールアドレスとパスワードでユーザーを認証する
  • Firestore コレクションにリスナーを追加して、UI が変更に反応するようにする
  • カスタム トレースを追加して、アプリ内の特定のコードのパフォーマンスをモニタリングする
  • Remote Config を使用して機能切り替えボタンを作成し、段階的な公開を使用してリリースする

ラボの内容

  • 最新の Android アプリケーションで Firebase Authentication、Performance Monitoring、Remote Config、Cloud Firestore を使用する方法
  • Firebase API を MVVM アーキテクチャに適合させる方法
  • Firebase API で行った変更を Compose UI に反映する方法

必要なもの

2. サンプルアプリを入手して Firebase を設定する

サンプルアプリのコードを取得する

コマンドラインから GitHub リポジトリのクローンを作成します。

git clone https://github.com/FirebaseExtended/make-it-so-android.git

Firebase を設定する

まず、Firebase コンソールに移動し、以下に示すように [+ プロジェクトを追加] ボタンをクリックして Firebase プロジェクトを作成します。

Firebase コンソール

画面の手順に沿ってプロジェクトの作成を完了します。

各 Firebase プロジェクト内で、Android、iOS、ウェブ、Flutter、Unity 用のさまざまなアプリを作成できます。以下に示すように、[Android] オプションを選択します。

Firebase プロジェクトの概要

続いて、次の手順を実行します。

  1. パッケージ名として「com.example.makeitso」と入力し、必要に応じてニックネームを入力します。この Codelab では、デバッグ用の署名証明書を追加する必要はありません。
  2. [次へ] をクリックしてアプリを登録し、Firebase 構成ファイルにアクセスします。
  3. [Download google-services.json] をクリックして構成ファイルをダウンロードし、make-it-so-android/app ディレクトリに保存します。
  4. [次へ] をクリックします。サンプル プロジェクトの build.gradle ファイルに Firebase SDK がすでに含まれているため、[次へ] をクリックして次のステップに進みます。
  5. [コンソールに進む] をクリックして終了します。

Make it So アプリを適切に動作させるには、コードに移動する前に、認証プロバイダを有効にして Firestore データベースを作成する 2 つのことをコンソールで行う必要があります。まず、認証を有効にして、ユーザーがアプリにログインできるようにしましょう。

  1. [Build] メニューから [Authentication] を選択し、[Get Started] をクリックします。
  2. [ログイン方法] カードで [メール/パスワード] を選択し、有効にします。
  3. 次に、[新しいプロバイダを追加] をクリックし、[匿名] を選択して有効にします。

次に、Firestore を設定します。Firestore を使用して、ログインしているユーザーのタスクを保存します。各ユーザーは、データベースのコレクション内の独自のドキュメントを取得します。

  1. [構築] メニューから [Firestore] を選択し、[データベースを作成] をクリックします。
  2. [本番環境モードで開始] は有効なままにして、[次へ] をクリックします。
  3. プロンプトが表示されたら、Cloud Firestore データを保存するロケーションを選択します。本番環境のアプリを開発するときは、大部分のユーザーに近いリージョンに配置し、Functions などの他の Firebase サービスとは共通で使用する必要があります。この Codelab では、デフォルトのリージョンをそのまま使用することも、最も近いリージョンを選択することもできます。
  4. [有効にする] をクリックして、Firestore データベースをプロビジョニングします。

ここで、Firestore データベースに対する堅牢なセキュリティ ルールを構築しましょう。Firestore ダッシュボードを開き、[ルール] タブに移動します。次に、セキュリティ ルールを次のように更新します。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null && resource.data.userId == request.auth.uid;
    }
  }
}

これらのルールは基本的に、アプリのログイン ユーザーが任意のコレクション内で自分用のドキュメントを作成できることを示します。作成されたドキュメントを表示、更新、削除できるのは、そのドキュメントを作成したユーザーのみです。

アプリケーションを実行する

これで、アプリケーションを実行する準備が整いました。Android Studio で make-it-so-android/start フォルダを開き、アプリを実行します(Android Emulator または実際の Android デバイスで実行できます)。

3. Firebase Authentication

どの機能を追加しますか?

Make It So サンプルアプリの現在の状態では、ユーザーは最初にログインすることなくアプリの使用を開始できます。これを実現するために匿名認証が使用されます。ただし、匿名アカウントでは、他のデバイスや今後のセッションでも、自分のデータにはアクセスできません。匿名認証はウォーム オンボーディングに役立ちますが、別のログイン方法に切り替えるオプションを常に提供する必要があります。これを念頭に置いて、この Codelab では、Make It So アプリにメールとパスワードによる認証を追加します。

いよいよコーディングです。

ユーザーがメールアドレスとパスワードを入力してアカウントを作成したらすぐに、Firebase Authentication API でメールの認証情報をリクエストし、新しい認証情報を匿名アカウントにリンクする必要があります。Android Studio で AccountServiceImpl.kt ファイルを開き、linkAccount 関数を次のように更新します。

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String) {
    val credential = EmailAuthProvider.getCredential(email, password)
    auth.currentUser!!.linkWithCredential(credential).await()
}

次に、SignUpViewModel.kt を開き、onSignUpClick 関数の launchCatching ブロック内でサービス linkAccount 関数を呼び出します。

screens/sign_up/SignUpViewModel.kt

launchCatching {
    accountService.linkAccount(email, password)
    openAndPopUp(SETTINGS_SCREEN, SIGN_UP_SCREEN)
}

最初に認証を試み、呼び出しが成功すると次の画面(SettingsScreen)に進みます。launchCatching ブロック内でこれらの呼び出しを実行しているため、1 行目でエラーが発生した場合、例外がキャッチされて処理され、2 行目にはまったく到達しません。

ユーザーはすでに認証されているので、SettingsScreen を再度開いたらすぐに、[ログイン] と [アカウントを作成] のオプションが表示されていないことを確認する必要があります。そのために、SettingsViewModel で現在のユーザーのステータス(AccountService.kt で利用可能)をリッスンし、アカウントが匿名かどうかを確認します。これを行うには、SettingsViewModel.ktuiState を次のように更新します。

screens/settings/SettingsViewModel.kt

val uiState = accountService.currentUser.map {
    SettingsUiState(it.isAnonymous)
}

最後に、SettingsScreen.ktuiState を更新して、SettingsViewModel が出力した状態を収集する必要があります。

screens/settings/SettingsScreen.kt

val uiState by viewModel.uiState.collectAsState(
    initial = SettingsUiState(false)
)

これで、ユーザーが変更されるたびに SettingsScreen が再コンポーズし、ユーザーの新しい認証状態に応じたオプションが表示されるようになりました。

さっそくテストしてみましょう。

[Make it So] を実行し、画面右上の歯車アイコンをクリックして設定に移動します。そこで、アカウントの作成オプションをクリックします。

Make It So の設定画面 Make It So の登録画面

有効なメールアドレスと安全なパスワードを入力してアカウントを作成してください。正常に機能するはずです。設定ページにリダイレクトされ、ログアウトとアカウントの削除という 2 つの新しいオプションが表示されます。作成した新しいアカウントは、Firebase コンソールの [Authentication] ダッシュボードで [ユーザー] タブをクリックすると確認できます。

4. Cloud Firestore

どの機能を追加しますか?

Cloud Firestore では、[Make it So] に表示されるタスクを表すドキュメントを格納するリスナーを Firestore コレクションに追加します。このリスナーを追加すると、このコレクションに対するすべての更新を受信します。

いよいよコーディングです。

StorageServiceImpl.kt で利用可能な Flow を次のように更新します。

model/service/impl/StorageServiceImpl.kt

override val tasks: Flow<List<Task>>
    get() =
      auth.currentUser.flatMapLatest { user ->
        firestore.collection(TASK_COLLECTION).whereEqualTo(USER_ID_FIELD, user.id).dataObjects()
      }

このコードは、user.id に基づいてタスク コレクションにリスナーを追加しています。各タスクは、tasks というコレクション内のドキュメントで表され、各タスクには userId というフィールドがあります。currentUser のステータスが(ログアウトするなどして)変化すると、新しい Flow が発行されます。

次に、TasksViewModel.ktFlow をサービス内と同じを反映させる必要があります。

screens/tasks/TasksViewModel.kt

val tasks = storageService.tasks

最後に、UI を表す TasksScreens.ktcomposable function でこのフローを認識し、状態として収集します。状態が変化するたびに、コンポーズ可能な関数は自動的に再コンポーズし、最新の状態をユーザーに表示します。これを TasksScreen composable function に追加します。

screens/tasks/TasksScreen.kt

val tasks = viewModel
    .tasks
    .collectAsStateWithLifecycle(emptyList())

コンポーズ可能な関数がこれらの状態にアクセスできるようになると、LazyColumn(画面にリストを表示するために使用する構造)を次のように更新できます。

screens/tasks/TasksScreen.kt

LazyColumn {
    items(tasks.value, key = { it.id }) { taskItem ->
        TaskItem( [...] )
    }
}

さっそくテストしてみましょう。

正しく機能するかどうかをテストするため、アプリを使用して(画面右下の追加ボタンをクリックして)新しいタスクを追加します。タスクの作成が完了すると、Firestore コンソールの Firestore コレクションにタスクが表示されます。同じアカウントを使用して他のデバイスで Make it So にログインすると、ToDo アイテムを編集し、すべてのデバイスで更新されるのをリアルタイムで確認できます。

5. Performance Monitoring

どの機能を追加しますか?

パフォーマンスが良くないと、ユーザーはアプリの使用をあきらめてしまい、アプリを使って簡単なタスクを完了するのに時間がかかりすぎてしまうため、パフォーマンスに注意を払うことは非常に重要です。そのため、アプリ内でユーザーがたどる特定のジャーニーに関する指標を収集すると便利な場合があります。そのために、Firebase Performance Monitoring にはカスタム トレースが用意されています。次のステップに沿って、Make it So でカスタム トレースを追加し、さまざまなコードのパフォーマンスを測定します。

いよいよコーディングです。

Performance.kt ファイルを開くと、trace というインライン関数が表示されます。この関数は、Performance Monitoring API を呼び出してカスタム トレースを作成し、トレース名をパラメータとして渡します。もう 1 つのパラメータは、モニタリングするコードブロックです。各トレースで収集されるデフォルトの指標は、完全に実行されるのにかかる時間です。

model/service/Performance.kt

inline fun <T> trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block)

コードベースのどの部分を測定すべきかを選択し、それにカスタム トレースを追加できます。以下は、この Codelab で前に(AccountServiceImpl.kt で)確認した linkAccount 関数にカスタム トレースを追加する例です。

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String): Unit =
  trace(LINK_ACCOUNT_TRACE) {
      val credential = EmailAuthProvider.getCredential(email, password)
      auth.currentUser!!.linkWithCredential(credential).await()
  }

それでは実際に試してみましょう。Make it So アプリにカスタム トレースをいくつか追加し、次のセクションに進んで、期待どおりに動作するかテストします。

さっそくテストしてみましょう。

カスタム トレースの追加が完了したら、アプリを実行し、測定する機能を数回使用します。次に、Firebase コンソールのパフォーマンス ダッシュボードに移動します。画面下部には、[ネットワーク リクエスト]、[カスタム トレース]、[画面レンダリング] の 3 つのタブがあります。

[カスタム トレース] タブに移動して、コードベースに追加したトレースがそこに表示されていることと、これらのコードの実行に通常かかる時間があることを確認します。

6. Remote Config

どの機能を追加しますか?

Remote Config には、アプリの外観をリモートで変更することから、ユーザー セグメントごとに異なる動作を設定することまで、さまざまなユースケースがあります。この Codelab では、Remote Config を使用して、Make it So アプリの新しいタスクの編集機能の表示と非表示を切り替える機能切り替えを作成します。

いよいよコーディングです。

まず、Firebase コンソールで構成を作成する必要があります。そのためには、Remote Config ダッシュボードに移動し、[パラメータを追加] ボタンをクリックします。以下の画像に沿って各フィールドに入力します。

Remote Config の [Create a Parameter] ダイアログ

すべての項目に入力したら、[保存] ボタンをクリックして [公開] をクリックします。これでパラメータが作成され、コードベースで使用できるようになったので、新しい値をフェッチするコードをアプリに追加する必要があります。ConfigurationServiceImpl.kt ファイルを開き、次の 2 つの関数の実装を更新します。

model/service/impl/ConfigurationServiceImpl.kt

override suspend fun fetchConfiguration(): Boolean {
  return remoteConfig.fetchAndActivate().await()
}

override val isShowTaskEditButtonConfig: Boolean
  get() = remoteConfig[SHOW_TASK_EDIT_BUTTON_KEY].asBoolean()

最初の関数はサーバーから値をフェッチし、アプリが起動するとすぐに SplashViewModel.kt で呼び出されます。これは、最初からすべての画面に最新の値が表示されるようにするための最善の方法です。後でユーザーが何かを行っているときに UI やアプリの動作を変更した場合、ユーザー エクスペリエンスは向上しません。

2 つ目の関数は、先ほどコンソールで作成したパラメータに対して公開されているブール値を返します。この情報は、loadTaskOptions 関数に以下を追加して、TasksViewModel.kt で取得する必要があります。

screens/tasks/TasksViewModel.kt

fun loadTaskOptions() {
  val hasEditOption = configurationService.isShowTaskEditButtonConfig
  options.value = TaskActionOption.getOptions(hasEditOption)
}

1 行目で値を取得し、その値を使用して 2 行目のタスクアイテムのメニュー オプションを読み込みます。値が false の場合は、メニューに編集オプションがないことを意味します。オプションのリストが用意できたので、UI に正しく表示させる必要があります。Jetpack Compose でアプリをビルドする場合、TasksScreen の UI がどのようになるかを宣言する composable function を探す必要があります。そこで、TasksScreen.kt ファイルを開き、TasksViewModel.kt で利用可能なオプションを指すように LazyColum を更新します。

screens/tasks/TasksScreen.kt

val options by viewModel.options

LazyColumn {
  items(tasks.value, key = { it.id }) { taskItem ->
    TaskItem(
      options = options,
      [...]
    )
  }
}

TaskItem は、単一タスクの UI の外観を宣言する別の composable function です。各タスクのメニューにはオプションがあり、ユーザーがタスクの最後にあるその他アイコンをクリックすると表示されます。

さっそくテストしてみましょう。

これで、アプリを実行する準備が整いました。Firebase コンソールを使用して公開した値がアプリの動作と一致していることを確認します。

  • false の場合、その他アイコンをクリックすると 2 つのオプションが表示されます。
  • true の場合は、その他アイコンをクリックすると 3 つのオプションが表示されます。

コンソールで値を数回変更してからアプリを再起動してみてください。Remote Config を使用すると、アプリで新機能を簡単にリリースできます。

7. 完了

これで、Firebase と Jetpack Compose を使用して Android アプリを作成することができました。

完全に UI 用に Jetpack Compose で構築された Android アプリに Firebase Authentication、Performance Monitoring、Remote Config、Cloud Firestore を追加し、推奨の MVVM アーキテクチャに適合させました。

参考資料

リファレンス ドキュメント