Firestoreの値をcallbackFlowとasLiveDataを組み合わせて使う
まとめ
- callbackFlowを使えばFirestoreのリアルタイムアップデートをFlow化できる
- FlowをasLiveDataする時はgetterじゃなくて代入するようにしないとViewが生きている間はFlowが永遠と生成される
callbackFlow
こんな感じでFireStore上のデータをコールバックからFlowへ変換してあげることができる
class Firestore() { fun hasType(type: String): Flow<Boolean> = callbackFlow { val doc = Firebase .firestore .collection("users") .document(userId) val callback = doc.addSnapshotListener { value, e -> // ここでよしなにデータを加工してあげる val result = if (e != null) { false } else { val types = value?.documents?.map { it.get("type").toString() } types?.contains(type) ?: false } // offerでFlowにデータを流す offer(result) } // awaitCloseでFlowがキャンセルされた時にコールバックの解放する処理を書く awaitClose { callback.remove() } }.distinctUntilChanged() }
asLiveData
ViewModel側でasLiveData()を使うとLiveDataに変換できる。
このとき、getterで指定すると新しいFlowがgetterにアクセスが有る度に生成されるからどんどんデータが流れてきてしまう。おとなしくインスタンスを代入してあげれば大丈夫。
あと、asLiveDataはcontextを指定しなければLiveDataがInActiveになっても5秒間生き残るのでviewModelScope.coroutineContextを渡しておいたほうが良さそう
class MyViewModel( private val firestore: Firestore ) : ViewModel() { // こっちはOK val isNewType: LiveData<Boolean> = firestore.hasType("newType") .asLiveData( context = viewModelScope.coroutineContext ) // こっちは無限にFlowがstartしてバンバンとデータが流れてきてしまうので注意!! val isNewType: LiveData<Boolean> get() = firestore.hasType("newType") .asLiveData( context = viewModelScope.coroutineContext ) }