LoginSignup
1
2

【Android/Kotlin】FirestoreのデータをFlowで簡単に返す方法

Last updated at Posted at 2023-08-31

はじめに

FirestoreでFlowを返したいときcallbackFlowを独自で実装するケースは多いと思います。

しかしながらFirebase Android BoMのバージョン30.5.0以上(firestore-ktxのバージョン24.3.1以上)であれば、便利な拡張関数が提供されています。

firestore-ktxが提供する拡張関数を使用することでわずか数行で記述することができます。

方法の比較

Repositoryのコードを例としてBefore(callbackFlowを実装する方法)とAfter(Firestore-ktxの拡張関数を使用する方法)を比べてみます。

class HogeRepositoryImpl @Inject constructor(
    private val db: FirebaseFirestore,
) : HogeRepository {

    companion object {
        private const val COLLECTION_PATH = "hoge"
    }

    // Before: callbackFlowを実装する方法
    override fun getHoge(): Flow<List<Hoge>> = callbackFlow {
        val collectionReference =
            db.collection(COLLECTION_PATH)

        val listener = collectionReference.addSnapshotListener { value, error ->
            if (error != null) {
                // エラー処理
            } else if (value != null) {
                val a = value.documents.mapNotNull { it.toObject<Hoge>() }
                trySendBlocking(a)
            }
        }
        awaitClose {
            listener.remove()
        }
    }

    //  After: 拡張関数を用いる方法
    override fun getHoge(): Flow<List<Hoge>> {
        val collectionReference = db.collection(COLLECTION_PATH)
        return collectionReference.dataObjects<Hoge>()
    }

Afterの拡張関数を用いる方法は、dataObjects()メソッドを用いて簡単にFlowを返せることがわかります。

dataObjects()の実装について

この便利なdataObjects()関数はどのように実装されているのでしょうか。

下記がdataObjects()メソッドの実装です。

inline fun <reified T : Any> Query.dataObjects(
  metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE
): Flow<List<T>> = snapshots(metadataChanges).map { it.toObjects(T::class.java) }

metadataChanges引数として受け取ります。これはメタデータの変更も受信するかの設定です。デフォルト引数ではメタデータの設定は除外するようにMetadataChages.EXCLUDEが設定されています。

snapshots()メソッドを呼び出し、返ってきた値をtoObjects()メソッドで指定の型パラメータに変換しています。

次にsnapshots()メソッドを見ていきます。

fun Query.snapshots(
  metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE
): Flow<QuerySnapshot> {
  return callbackFlow {
    val registration =
      addSnapshotListener(BACKGROUND_EXECUTOR, metadataChanges) { snapshot, exception ->
        if (exception != null) {
          cancel(message = "Error getting Query snapshot", cause = exception)
        } else if (snapshot != null) {
          trySendBlocking(snapshot)
        }
      }
    awaitClose { registration.remove() }
  }
}

callbackFlowを使用していることがわかると思います。addSnapshotListener()メソッドで変更をリッスンし、値があればtrySendBlockingメソッドで値を流しています。

おわりに

以上です。便利な拡張関数を使用してコードがすっきできると嬉しいですね。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2