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

1. はじめに

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

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

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

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

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

作成するアプリの概要

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

Make it So のタスク追加画面 Make it So のホーム画面

このアプリに欠けている機能を追加します。

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

ラボの内容

  • 最新の Android アプリで Firebase Authentication、Performance Monitoring、Firebase 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 アプリを追加する

Firebase プロジェクトでは、Android、iOS、ウェブ、Flutter、Unity 用のさまざまなアプリを登録できます。

次のように、[Android] オプションを選択します。

Firebase プロジェクトの概要

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

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

Make it So アプリを正常に動作させるには、コードに進む前にコンソールで 2 つの操作を行う必要があります。認証プロバイダを有効にし、Firestore データベースを作成します。

認証を設定する

まず、ユーザーがアプリにログインできるように認証を有効にします。

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

Cloud Firestore を設定する

次に、Firestore を設定します。Firestore を使用して、ログイン中のユーザーのタスクを保存します。各ユーザーには、データベースのコレクション内に独自のドキュメントが割り当てられます。

  1. Firebase コンソールの左側のパネルで [Build] を開き、[Firestore データベース] を選択します。
  2. [データベースを作成] をクリックします。
  3. [データベース ID] は (default) のままにします。
  4. データベースのロケーションを選択し、[次へ] をクリックします。
    実際のアプリの場合は、ユーザーに近いロケーションを選択します。
  5. [テストモードで開始] をクリックします。セキュリティ ルールに関する免責条項を確認します。
    このセクションの次の手順では、セキュリティ ルールを追加してデータを保護します。データベースのセキュリティ ルールを追加せずに、アプリを配布または公開しないでください。
  6. [作成] をクリックします。

では、Firestore データベースに堅牢なセキュリティ ルールを作成しましょう。

  1. Firestore ダッシュボードを開き、[ルール] タブに移動します。
  2. セキュリティ ルールを次のように更新します。
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] ダッシュボードで [Users] タブをクリックして確認できます。

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 コレクションにタスクが表示されます。同じアカウントで別のデバイスで ToDo リストにログインすると、ToDo リストを編集し、すべてのデバイスでリアルタイムに更新を確認できます。

5. パフォーマンス モニタリング

追加する機能はどれですか?

パフォーマンスは非常に重要な要素です。パフォーマンスが低く、簡単なタスクの完了に時間がかかりすぎると、ユーザーはアプリの使用を放棄する可能性が高くなります。そのため、アプリ内でユーザーが行う特定のフローに関する指標を収集することが有用な場合があります。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 の [パラメータを作成] ダイアログ

すべてのフィールドに入力したら、[保存] ボタンをクリックし、[公開] をクリックします。パラメータが作成され、コードベースで使用できるようになったので、新しい値をフェッチするコードをアプリに追加する必要があります。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 番目の関数は、コンソールで作成したパラメータに対して公開されたブール値を返します。この情報を TasksViewModel.kt で取得するには、loadTaskOptions 関数に次のコードを追加します。

screens/tasks/TasksViewModel.kt

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

最初の行で値を取得し、その値を使用して 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 つのオプションが表示されます。

Console で値を数回変更してアプリを再起動してみてください。Remote Config を使用してアプリで新機能をリリースするのは、これほど簡単です。

7. 完了

Firebase と Jetpack Compose を使用して Android アプリを正常に作成できました。

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

参考資料

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