はじめに
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
メソッドで値を流しています。
おわりに
以上です。便利な拡張関数を使用してコードがすっきできると嬉しいですね。