14
14
15
15
package com.google.firebase.firestore.ktx
16
16
17
+ import android.support.multidex.BuildConfig
17
18
import androidx.annotation.Keep
18
19
import com.google.firebase.FirebaseApp
19
20
import com.google.firebase.components.Component
20
21
import com.google.firebase.components.ComponentRegistrar
21
- import com.google.firebase.firestore.DocumentSnapshot
22
- import com.google.firebase.firestore.FieldPath
23
- import com.google.firebase.firestore.FirebaseFirestore
24
- import com.google.firebase.firestore.FirebaseFirestoreSettings
25
- import com.google.firebase.firestore.QueryDocumentSnapshot
26
- import com.google.firebase.firestore.QuerySnapshot
27
- import com.google.firebase.firestore.FirebaseFirestoreSettings
28
- import com.google.firebase.firestore.MetadataChanges
29
- import com.google.firebase.firestore.ListenerRegistration
22
+ import com.google.firebase.firestore.*
30
23
import com.google.firebase.ktx.Firebase
31
24
import com.google.firebase.platforminfo.LibraryVersionComponent
32
- import kotlinx.coroutines.Dispatchers
33
- import kotlinx.coroutines.NonCancellable
25
+ import kotlinx.coroutines.*
34
26
import kotlinx.coroutines.channels.Channel
27
+ import kotlinx.coroutines.channels.awaitClose
35
28
import kotlinx.coroutines.flow.Flow
29
+ import kotlinx.coroutines.flow.buffer
30
+ import kotlinx.coroutines.flow.callbackFlow
36
31
import kotlinx.coroutines.flow.flow
37
- import kotlinx.coroutines.withContext
38
32
39
33
/* * Returns the [FirebaseFirestore] instance of the default [FirebaseApp]. */
40
34
val Firebase .firestore: FirebaseFirestore
@@ -173,37 +167,69 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar {
173
167
}
174
168
175
169
/* *
176
- * Attach a snapshotListener to a DocumentReference and use it as a coroutine flow
177
- * @param metadataChanges Indicates whether metadata-only changes
170
+ * Transforms a [DocumentReference] into a coroutine [Flow]
171
+ *
172
+ * **Backpressure handling**: by default this method conflates items. If the consumer isn't fast enough,
173
+ * it might miss some values but is always guaranteed to get the latest value. Use [bufferCapacity]
174
+ * to change that behaviour
175
+ *
176
+ * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE]
177
+ * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all
178
178
*/
179
- fun DocumentReference.toFlow (metadataChanges : MetadataChanges = MetadataChanges .EXCLUDE ) =
180
- callbackFlow {
179
+ fun DocumentReference.toFlow (
180
+ metadataChanges : MetadataChanges = MetadataChanges .EXCLUDE ,
181
+ bufferCapacity : Int? = Channel .CONFLATED
182
+ ): Flow <DocumentSnapshot ?> {
183
+ val flow = callbackFlow {
181
184
val registration = addSnapshotListener(metadataChanges) { snapshot, exception ->
182
185
if (exception != null ) {
183
- cancel(message = " Error getting DocumentReference snapshot" , cause = exception)
186
+ cancel(CancellationException ( " Error getting DocumentReference snapshot" , exception) )
184
187
}
185
188
186
189
if (snapshot != null ) {
187
190
trySend(snapshot)
188
191
}
189
192
}
190
193
awaitClose { registration.remove() }
191
- }.buffer(Channel .CONFLATED )
194
+ }
195
+
196
+ return if (bufferCapacity != null ) {
197
+ flow.buffer(bufferCapacity)
198
+ } else {
199
+ flow
200
+ }
201
+ }
192
202
193
203
/* *
194
- * Attach a snapshotListener to a Query and use it as a coroutine flow
195
- * @param metadataChanges Indicates whether metadata-only changes
204
+ * Transforms a [Query] into a coroutine [Flow]
205
+ *
206
+ * **Backpressure handling**: by default this method conflates items. If the consumer isn't fast enough,
207
+ * it might miss some values but is always guaranteed to get the latest value. Use [bufferCapacity]
208
+ * to change that behaviour
209
+ *
210
+ * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE]
211
+ * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all
196
212
*/
197
- fun Query.toFlow (metadataChanges : MetadataChanges = MetadataChanges .EXCLUDE ) =
198
- callbackFlow {
213
+ fun Query.toFlow (
214
+ metadataChanges : MetadataChanges = MetadataChanges .EXCLUDE ,
215
+ bufferCapacity : Int? = Channel .CONFLATED
216
+ ): Flow <QuerySnapshot ?> {
217
+ val flow = callbackFlow {
199
218
val registration = addSnapshotListener(metadataChanges) { snapshot, exception ->
200
219
if (exception != null ) {
201
- cancel(message = " Error getting Query snapshot" , cause = exception)
220
+ cancel(CancellationException ( " Error getting Query snapshot" , exception) )
202
221
}
203
222
204
223
if (snapshot != null ) {
205
224
trySend(snapshot)
206
225
}
207
226
}
208
227
awaitClose { registration.remove() }
209
- }.buffer(Channel .CONFLATED )
228
+ }
229
+
230
+ return if (bufferCapacity != null ) {
231
+ flow.buffer(bufferCapacity)
232
+ } else {
233
+ flow
234
+ }
235
+ }
0 commit comments