From af0f46936594dcc830319bfacf585729b41fb682 Mon Sep 17 00:00:00 2001 From: Michel Gauzins Date: Tue, 18 Feb 2020 15:32:44 +0100 Subject: [PATCH 01/23] Add toFlow extensions to DocumentSnapshot and Query --- firebase-firestore/ktx/ktx.gradle | 1 + .../firebase/firestore/ktx/Firestore.kt | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/firebase-firestore/ktx/ktx.gradle b/firebase-firestore/ktx/ktx.gradle index fdc573edaa3..2c89d945063 100644 --- a/firebase-firestore/ktx/ktx.gradle +++ b/firebase-firestore/ktx/ktx.gradle @@ -55,6 +55,7 @@ dependencies { implementation project(':firebase-common:ktx') implementation project(':firebase-firestore') implementation 'androidx.annotation:annotation:1.1.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' testImplementation project(':firebase-database-collection') testImplementation 'org.mockito:mockito-core:2.25.0' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index ec2ee225d41..9abfddd7795 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -26,6 +26,8 @@ import com.google.firebase.firestore.QueryDocumentSnapshot import com.google.firebase.firestore.QuerySnapshot import com.google.firebase.ktx.Firebase import com.google.firebase.platforminfo.LibraryVersionComponent +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.callbackFlow /** Returns the [FirebaseFirestore] instance of the default [FirebaseApp]. */ val Firebase.firestore: FirebaseFirestore @@ -162,3 +164,35 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { override fun getComponents(): List> = listOf(LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME)) } + +/** + * Attach a snapshotListener to a DocumentReference and use it as a coroutine flow + */ +fun DocumentReference.toFlow() = callbackFlow { + val listener = addSnapshotListener { value, error -> + if (value != null && value.exists()) { + offer(value) + } else if (error != null) { + Logger.warn("DocumentReference:flow", error.message) + } + } + awaitClose { + listener.remove() + } +} + +/** + * Attach a snapshotListener to a Query and use it as a coroutine flow + */ +fun Query.toFlow() = callbackFlow { + val listener = addSnapshotListener { value, error -> + if (value != null) { + offer(value) + } else if (error != null) { + Logger.warn("Query:flow", error.message) + } + } + awaitClose { + listener.remove() + } +} \ No newline at end of file From 931e73f6f773992b6eda883f9bde018ab8052897 Mon Sep 17 00:00:00 2001 From: Michel Gauzins Date: Wed, 19 Feb 2020 09:47:19 +0100 Subject: [PATCH 02/23] Add toFlow extensions to DocumentSnapshot and Query --- .../kotlin/com/google/firebase/firestore/ktx/Firestore.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 9abfddd7795..6dcd3006c7f 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -173,7 +173,7 @@ fun DocumentReference.toFlow() = callbackFlow { if (value != null && value.exists()) { offer(value) } else if (error != null) { - Logger.warn("DocumentReference:flow", error.message) + close(error) } } awaitClose { @@ -189,7 +189,7 @@ fun Query.toFlow() = callbackFlow { if (value != null) { offer(value) } else if (error != null) { - Logger.warn("Query:flow", error.message) + close(error) } } awaitClose { From 57fbe74708cde01cf97f9179267d36ac185862e4 Mon Sep 17 00:00:00 2001 From: Michel Gauzins Date: Wed, 19 Feb 2020 10:23:34 +0100 Subject: [PATCH 03/23] Update API Txt file --- firebase-firestore/ktx/api.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firebase-firestore/ktx/api.txt b/firebase-firestore/ktx/api.txt index 634bb8923d9..9adfe8df48d 100644 --- a/firebase-firestore/ktx/api.txt +++ b/firebase-firestore/ktx/api.txt @@ -10,6 +10,8 @@ package com.google.firebase.firestore.ktx { method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath); method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static com.google.firebase.firestore.FirebaseFirestore getFirestore(@NonNull com.google.firebase.ktx.Firebase); + method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.DocumentReference); + method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.Query); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static inline T toObject(@NonNull com.google.firebase.firestore.QueryDocumentSnapshot); From c87ea63da6dbea8a71ae82fd5a6aa8a61050c817 Mon Sep 17 00:00:00 2001 From: Michel Gauzins Date: Wed, 19 Feb 2020 10:24:49 +0100 Subject: [PATCH 04/23] Implement 'com.google.android.gms:play-services-base' to retrieve FirebaseException --- firebase-firestore/ktx/ktx.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/firebase-firestore/ktx/ktx.gradle b/firebase-firestore/ktx/ktx.gradle index 2c89d945063..611614b8f58 100644 --- a/firebase-firestore/ktx/ktx.gradle +++ b/firebase-firestore/ktx/ktx.gradle @@ -56,6 +56,7 @@ dependencies { implementation project(':firebase-firestore') implementation 'androidx.annotation:annotation:1.1.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' + implementation 'com.google.android.gms:play-services-base:17.0.0' testImplementation project(':firebase-database-collection') testImplementation 'org.mockito:mockito-core:2.25.0' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' From fe0916c68117ce922d71f3f6578418743651abd3 Mon Sep 17 00:00:00 2001 From: Michel Gauzins Date: Tue, 25 Feb 2020 16:39:43 +0100 Subject: [PATCH 05/23] Add MetadataChanges optional parameter --- firebase-firestore/ktx/api.txt | 4 ++-- .../com/google/firebase/firestore/ktx/Firestore.kt | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/firebase-firestore/ktx/api.txt b/firebase-firestore/ktx/api.txt index 9adfe8df48d..a551615267e 100644 --- a/firebase-firestore/ktx/api.txt +++ b/firebase-firestore/ktx/api.txt @@ -10,8 +10,8 @@ package com.google.firebase.firestore.ktx { method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath); method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static com.google.firebase.firestore.FirebaseFirestore getFirestore(@NonNull com.google.firebase.ktx.Firebase); - method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.DocumentReference); - method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.Query); + method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.DocumentReference, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE); + method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.Query, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static inline T toObject(@NonNull com.google.firebase.firestore.QueryDocumentSnapshot); diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 6dcd3006c7f..d6529d09df1 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -24,6 +24,8 @@ import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.FirebaseFirestoreSettings import com.google.firebase.firestore.QueryDocumentSnapshot import com.google.firebase.firestore.QuerySnapshot +import com.google.firebase.firestore.FirebaseFirestoreSettings +import com.google.firebase.firestore.MetadataChanges import com.google.firebase.ktx.Firebase import com.google.firebase.platforminfo.LibraryVersionComponent import kotlinx.coroutines.channels.awaitClose @@ -167,9 +169,10 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { /** * Attach a snapshotListener to a DocumentReference and use it as a coroutine flow + * @param metadataChanges Indicates whether metadata-only changes */ -fun DocumentReference.toFlow() = callbackFlow { - val listener = addSnapshotListener { value, error -> +fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = callbackFlow { + val listener = addSnapshotListener(metadataChanges) { value, error -> if (value != null && value.exists()) { offer(value) } else if (error != null) { @@ -183,9 +186,10 @@ fun DocumentReference.toFlow() = callbackFlow { /** * Attach a snapshotListener to a Query and use it as a coroutine flow + * @param metadataChanges Indicates whether metadata-only changes */ -fun Query.toFlow() = callbackFlow { - val listener = addSnapshotListener { value, error -> +fun Query.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = callbackFlow { + val listener = addSnapshotListener(metadataChanges) { value, error -> if (value != null) { offer(value) } else if (error != null) { From 15bb8b932749ac04a6e66faf41ecd5ad7aae2664 Mon Sep 17 00:00:00 2001 From: Michel Gauzins Date: Wed, 26 Feb 2020 09:21:37 +0100 Subject: [PATCH 06/23] Wrap 'offer()' in runCatching --- .../google/firebase/firestore/ktx/Firestore.kt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index d6529d09df1..c5fd0dd8435 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -174,7 +174,14 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = callbackFlow { val listener = addSnapshotListener(metadataChanges) { value, error -> if (value != null && value.exists()) { - offer(value) + /** + * Offer will throw if the channel is canceled. + * To avoid that, we wrap the call in runCatching. + * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 + */ + runCatching { + offer(value) + } } else if (error != null) { close(error) } @@ -191,7 +198,14 @@ fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges. fun Query.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = callbackFlow { val listener = addSnapshotListener(metadataChanges) { value, error -> if (value != null) { - offer(value) + /** + * Offer will throw if the channel is canceled. + * To avoid that, we wrap the call in runCatching. + * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 + */ + runCatching { + offer(value) + } } else if (error != null) { close(error) } From fcfb4b1a39bfd576a427deb6a0d0a69a017880d0 Mon Sep 17 00:00:00 2001 From: Michel Gauzins Date: Mon, 22 Jun 2020 15:50:01 +0200 Subject: [PATCH 07/23] Use 'flow' and 'Channel' instead of unstable 'callbackFlow' --- .../firebase/firestore/ktx/Firestore.kt | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index c5fd0dd8435..66e00d44f03 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -26,10 +26,15 @@ import com.google.firebase.firestore.QueryDocumentSnapshot import com.google.firebase.firestore.QuerySnapshot import com.google.firebase.firestore.FirebaseFirestoreSettings import com.google.firebase.firestore.MetadataChanges +import com.google.firebase.firestore.ListenerRegistration import com.google.firebase.ktx.Firebase import com.google.firebase.platforminfo.LibraryVersionComponent -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.withContext /** Returns the [FirebaseFirestore] instance of the default [FirebaseApp]. */ val Firebase.firestore: FirebaseFirestore @@ -171,23 +176,23 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { * Attach a snapshotListener to a DocumentReference and use it as a coroutine flow * @param metadataChanges Indicates whether metadata-only changes */ -fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = callbackFlow { - val listener = addSnapshotListener(metadataChanges) { value, error -> - if (value != null && value.exists()) { - /** - * Offer will throw if the channel is canceled. - * To avoid that, we wrap the call in runCatching. - * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 - */ - runCatching { - offer(value) - } - } else if (error != null) { - close(error) +fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = flow { + val channel = Channel(Channel.CONFLATED) + var listener: ListenerRegistration? = null + withContext(Dispatchers.Main.immediate) { + listener = addSnapshotListener(metadataChanges) { value, error -> + value?.let { channel.offer(it) } + error?.let { channel.close(it) } } } - awaitClose { - listener.remove() + try { + for (value in channel) { + emit(value) + } + } finally { + withContext(Dispatchers.Main.immediate + NonCancellable) { + listener?.remove() + } } } @@ -195,22 +200,22 @@ fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges. * Attach a snapshotListener to a Query and use it as a coroutine flow * @param metadataChanges Indicates whether metadata-only changes */ -fun Query.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = callbackFlow { - val listener = addSnapshotListener(metadataChanges) { value, error -> - if (value != null) { - /** - * Offer will throw if the channel is canceled. - * To avoid that, we wrap the call in runCatching. - * See https://github.com/Kotlin/kotlinx.coroutines/issues/974 - */ - runCatching { - offer(value) - } - } else if (error != null) { - close(error) +fun Query.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE): Flow = flow { + val channel = Channel(Channel.CONFLATED) + var listener: ListenerRegistration? = null + withContext(Dispatchers.Main.immediate) { + listener = addSnapshotListener(metadataChanges) { value, error -> + value?.let { channel.offer(it) } + error?.let { channel.close(it) } } } - awaitClose { - listener.remove() + try { + for (value in channel) { + emit(value) + } + } finally { + withContext(Dispatchers.Main.immediate + NonCancellable) { + listener?.remove() + } } } \ No newline at end of file From ad9db4d6ba51cd564a1fa60ed73958e331dde9e2 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 2 Jun 2022 14:39:38 +0200 Subject: [PATCH 08/23] pull the version from @svenjacobs --- .../firebase/firestore/ktx/Firestore.kt | 60 ++++++++----------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 66e00d44f03..b07c4b05edc 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -176,46 +176,34 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { * Attach a snapshotListener to a DocumentReference and use it as a coroutine flow * @param metadataChanges Indicates whether metadata-only changes */ -fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = flow { - val channel = Channel(Channel.CONFLATED) - var listener: ListenerRegistration? = null - withContext(Dispatchers.Main.immediate) { - listener = addSnapshotListener(metadataChanges) { value, error -> - value?.let { channel.offer(it) } - error?.let { channel.close(it) } +fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = + callbackFlow { + val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> + if (exception != null) { + cancel(message = "Error getting DocumentReference snapshot", cause = exception) + } + + if (snapshot != null) { + trySend(snapshot) + } } - } - try { - for (value in channel) { - emit(value) - } - } finally { - withContext(Dispatchers.Main.immediate + NonCancellable) { - listener?.remove() - } - } -} + awaitClose { registration.remove() } + }.buffer(Channel.CONFLATED) /** * Attach a snapshotListener to a Query and use it as a coroutine flow * @param metadataChanges Indicates whether metadata-only changes */ -fun Query.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE): Flow = flow { - val channel = Channel(Channel.CONFLATED) - var listener: ListenerRegistration? = null - withContext(Dispatchers.Main.immediate) { - listener = addSnapshotListener(metadataChanges) { value, error -> - value?.let { channel.offer(it) } - error?.let { channel.close(it) } - } - } - try { - for (value in channel) { - emit(value) - } - } finally { - withContext(Dispatchers.Main.immediate + NonCancellable) { - listener?.remove() +fun Query.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = + callbackFlow { + val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> + if (exception != null) { + cancel(message = "Error getting Query snapshot", cause = exception) + } + + if (snapshot != null) { + trySend(snapshot) + } } - } -} \ No newline at end of file + awaitClose { registration.remove() } + }.buffer(Channel.CONFLATED) From 4d7237d21d8499a5f4e6bda6870f34fabdf63960 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 2 Jun 2022 15:13:32 +0200 Subject: [PATCH 09/23] add "bufferCapacity" as a parameter --- firebase-firestore/ktx/ktx.gradle | 2 +- .../firebase/firestore/ktx/Firestore.kt | 74 +++++++++++++------ 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/firebase-firestore/ktx/ktx.gradle b/firebase-firestore/ktx/ktx.gradle index 611614b8f58..613a649353d 100644 --- a/firebase-firestore/ktx/ktx.gradle +++ b/firebase-firestore/ktx/ktx.gradle @@ -55,7 +55,7 @@ dependencies { implementation project(':firebase-common:ktx') implementation project(':firebase-firestore') implementation 'androidx.annotation:annotation:1.1.0' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1' implementation 'com.google.android.gms:play-services-base:17.0.0' testImplementation project(':firebase-database-collection') testImplementation 'org.mockito:mockito-core:2.25.0' diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index b07c4b05edc..e247cdec851 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -14,27 +14,21 @@ package com.google.firebase.firestore.ktx +import android.support.multidex.BuildConfig import androidx.annotation.Keep import com.google.firebase.FirebaseApp import com.google.firebase.components.Component import com.google.firebase.components.ComponentRegistrar -import com.google.firebase.firestore.DocumentSnapshot -import com.google.firebase.firestore.FieldPath -import com.google.firebase.firestore.FirebaseFirestore -import com.google.firebase.firestore.FirebaseFirestoreSettings -import com.google.firebase.firestore.QueryDocumentSnapshot -import com.google.firebase.firestore.QuerySnapshot -import com.google.firebase.firestore.FirebaseFirestoreSettings -import com.google.firebase.firestore.MetadataChanges -import com.google.firebase.firestore.ListenerRegistration +import com.google.firebase.firestore.* import com.google.firebase.ktx.Firebase import com.google.firebase.platforminfo.LibraryVersionComponent -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.NonCancellable +import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.withContext /** Returns the [FirebaseFirestore] instance of the default [FirebaseApp]. */ val Firebase.firestore: FirebaseFirestore @@ -173,14 +167,23 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { } /** - * Attach a snapshotListener to a DocumentReference and use it as a coroutine flow - * @param metadataChanges Indicates whether metadata-only changes + * Transforms a [DocumentReference] into a coroutine [Flow] + * + * **Backpressure handling**: by default this method conflates items. If the consumer isn't fast enough, + * it might miss some values but is always guaranteed to get the latest value. Use [bufferCapacity] + * to change that behaviour + * + * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] + * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all */ -fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = - callbackFlow { +fun DocumentReference.toFlow( + metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, + bufferCapacity: Int? = Channel.CONFLATED +): Flow { + val flow = callbackFlow { val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> if (exception != null) { - cancel(message = "Error getting DocumentReference snapshot", cause = exception) + cancel(CancellationException("Error getting DocumentReference snapshot", exception)) } if (snapshot != null) { @@ -188,17 +191,33 @@ fun DocumentReference.toFlow(metadataChanges: MetadataChanges = MetadataChanges. } } awaitClose { registration.remove() } - }.buffer(Channel.CONFLATED) + } + + return if (bufferCapacity != null) { + flow.buffer(bufferCapacity) + } else { + flow + } +} /** - * Attach a snapshotListener to a Query and use it as a coroutine flow - * @param metadataChanges Indicates whether metadata-only changes + * Transforms a [Query] into a coroutine [Flow] + * + * **Backpressure handling**: by default this method conflates items. If the consumer isn't fast enough, + * it might miss some values but is always guaranteed to get the latest value. Use [bufferCapacity] + * to change that behaviour + * + * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] + * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all */ -fun Query.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = - callbackFlow { +fun Query.toFlow( + metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, + bufferCapacity: Int? = Channel.CONFLATED +): Flow { + val flow = callbackFlow { val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> if (exception != null) { - cancel(message = "Error getting Query snapshot", cause = exception) + cancel(CancellationException("Error getting Query snapshot", exception)) } if (snapshot != null) { @@ -206,4 +225,11 @@ fun Query.toFlow(metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE) = } } awaitClose { registration.remove() } - }.buffer(Channel.CONFLATED) + } + + return if (bufferCapacity != null) { + flow.buffer(bufferCapacity) + } else { + flow + } +} \ No newline at end of file From de32290bfa9d4e07c42d15d8a40bf8b08116b501 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 2 Jun 2022 15:22:46 +0200 Subject: [PATCH 10/23] remove wildcard imports --- .../com/google/firebase/firestore/ktx/Firestore.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index e247cdec851..477c54a75cc 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -19,16 +19,24 @@ import androidx.annotation.Keep import com.google.firebase.FirebaseApp import com.google.firebase.components.Component import com.google.firebase.components.ComponentRegistrar -import com.google.firebase.firestore.* +import com.google.firebase.firestore.DocumentReference +import com.google.firebase.firestore.DocumentSnapshot +import com.google.firebase.firestore.FieldPath +import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.FirebaseFirestoreSettings +import com.google.firebase.firestore.MetadataChanges +import com.google.firebase.firestore.Query +import com.google.firebase.firestore.QueryDocumentSnapshot +import com.google.firebase.firestore.QuerySnapshot import com.google.firebase.ktx.Firebase import com.google.firebase.platforminfo.LibraryVersionComponent -import kotlinx.coroutines.* +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.flow /** Returns the [FirebaseFirestore] instance of the default [FirebaseApp]. */ val Firebase.firestore: FirebaseFirestore From cccf86ff95d8ff0ab02dfeddabef3fd3d05bafc7 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Mon, 6 Jun 2022 11:32:01 +0200 Subject: [PATCH 11/23] use cancel(message, cause) --- .../kotlin/com/google/firebase/firestore/ktx/Firestore.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 477c54a75cc..871dbce5fc8 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -191,7 +191,7 @@ fun DocumentReference.toFlow( val flow = callbackFlow { val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> if (exception != null) { - cancel(CancellationException("Error getting DocumentReference snapshot", exception)) + cancel(message = "Error getting DocumentReference snapshot", cause = exception) } if (snapshot != null) { @@ -225,7 +225,7 @@ fun Query.toFlow( val flow = callbackFlow { val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> if (exception != null) { - cancel(CancellationException("Error getting Query snapshot", exception)) + cancel(message = "Error getting Query snapshot", cause = exception) } if (snapshot != null) { From c91731abc512aa3c34f09c49b068adbbdf1681d4 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 4 Aug 2022 12:24:11 +0200 Subject: [PATCH 12/23] make compile --- firebase-firestore/firebase-firestore.gradle | 2 +- firebase-firestore/ktx/ktx.gradle | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/firebase-firestore/firebase-firestore.gradle b/firebase-firestore/firebase-firestore.gradle index 9046ae66cb4..ef781dce236 100644 --- a/firebase-firestore/firebase-firestore.gradle +++ b/firebase-firestore/firebase-firestore.gradle @@ -144,7 +144,7 @@ dependencies { implementation "io.grpc:grpc-protobuf-lite:$grpcVersion" implementation "io.grpc:grpc-okhttp:$grpcVersion" implementation "io.grpc:grpc-android:$grpcVersion" - implementation 'com.google.android.gms:play-services-basement:18.0.0' + api 'com.google.android.gms:play-services-basement:18.0.0' implementation 'com.google.android.gms:play-services-tasks:18.0.1' implementation 'com.google.android.gms:play-services-base:18.0.1' diff --git a/firebase-firestore/ktx/ktx.gradle b/firebase-firestore/ktx/ktx.gradle index 613a649353d..dca5246703a 100644 --- a/firebase-firestore/ktx/ktx.gradle +++ b/firebase-firestore/ktx/ktx.gradle @@ -55,8 +55,7 @@ dependencies { implementation project(':firebase-common:ktx') implementation project(':firebase-firestore') implementation 'androidx.annotation:annotation:1.1.0' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1' - implementation 'com.google.android.gms:play-services-base:17.0.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' testImplementation project(':firebase-database-collection') testImplementation 'org.mockito:mockito-core:2.25.0' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' From a3d04ac51355c0c81a116611ade36d2e50516945 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 4 Aug 2022 12:38:49 +0200 Subject: [PATCH 13/23] update public API --- firebase-firestore/ktx/api.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase-firestore/ktx/api.txt b/firebase-firestore/ktx/api.txt index a551615267e..a049defbd8f 100644 --- a/firebase-firestore/ktx/api.txt +++ b/firebase-firestore/ktx/api.txt @@ -10,8 +10,8 @@ package com.google.firebase.firestore.ktx { method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath); method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static com.google.firebase.firestore.FirebaseFirestore getFirestore(@NonNull com.google.firebase.ktx.Firebase); - method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.DocumentReference, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE); - method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.Query, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE); + method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.DocumentReference, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE, @Nullable Integer bufferCapacity = -1); + method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.Query, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE, @Nullable Integer bufferCapacity = -1); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static inline T toObject(@NonNull com.google.firebase.firestore.QueryDocumentSnapshot); From 31d15536a34e368c1fba33dd85c1cd5e583323da Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 4 Aug 2022 12:56:27 +0200 Subject: [PATCH 14/23] fix lint --- .../main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 871dbce5fc8..db274d29bec 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -30,7 +30,6 @@ import com.google.firebase.firestore.QueryDocumentSnapshot import com.google.firebase.firestore.QuerySnapshot import com.google.firebase.ktx.Firebase import com.google.firebase.platforminfo.LibraryVersionComponent -import kotlinx.coroutines.CancellationException import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.awaitClose @@ -240,4 +239,4 @@ fun Query.toFlow( } else { flow } -} \ No newline at end of file +} From 2e51a63f3c4ec84c677c846897dc5cff2dd14c20 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 4 Aug 2022 18:34:42 +0200 Subject: [PATCH 15/23] return non nullable snapshots --- .../com/google/firebase/firestore/ktx/Firestore.kt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index db274d29bec..2a7a6be6d3b 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -186,14 +186,12 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { fun DocumentReference.toFlow( metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, bufferCapacity: Int? = Channel.CONFLATED -): Flow { +): Flow { val flow = callbackFlow { val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> if (exception != null) { cancel(message = "Error getting DocumentReference snapshot", cause = exception) - } - - if (snapshot != null) { + } else if (snapshot != null) { trySend(snapshot) } } @@ -220,14 +218,12 @@ fun DocumentReference.toFlow( fun Query.toFlow( metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, bufferCapacity: Int? = Channel.CONFLATED -): Flow { +): Flow { val flow = callbackFlow { val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> if (exception != null) { cancel(message = "Error getting Query snapshot", cause = exception) - } - - if (snapshot != null) { + } else if (snapshot != null) { trySend(snapshot) } } From 288ae9aed3ea4691aac5efab5bcbdddd999e5af8 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Tue, 9 Aug 2022 20:27:18 +0200 Subject: [PATCH 16/23] toFlow -> snapshots --- firebase-firestore/ktx/api.txt | 4 ++-- .../kotlin/com/google/firebase/firestore/ktx/Firestore.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/firebase-firestore/ktx/api.txt b/firebase-firestore/ktx/api.txt index a049defbd8f..4e8c7b10668 100644 --- a/firebase-firestore/ktx/api.txt +++ b/firebase-firestore/ktx/api.txt @@ -10,8 +10,8 @@ package com.google.firebase.firestore.ktx { method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath); method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static com.google.firebase.firestore.FirebaseFirestore getFirestore(@NonNull com.google.firebase.ktx.Firebase); - method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.DocumentReference, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE, @Nullable Integer bufferCapacity = -1); - method @NonNull public static kotlinx.coroutines.flow.Flow toFlow(@NonNull com.google.firebase.firestore.Query, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE, @Nullable Integer bufferCapacity = -1); + method @NonNull public static kotlinx.coroutines.flow.Flow snapshots(@NonNull com.google.firebase.firestore.DocumentReference, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE, @Nullable Integer bufferCapacity = -1); + method @NonNull public static kotlinx.coroutines.flow.Flow snapshots(@NonNull com.google.firebase.firestore.Query, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE, @Nullable Integer bufferCapacity = -1); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static inline T toObject(@NonNull com.google.firebase.firestore.QueryDocumentSnapshot); diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 2a7a6be6d3b..b02639683d3 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -183,7 +183,7 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all */ -fun DocumentReference.toFlow( +fun DocumentReference.snapshots( metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, bufferCapacity: Int? = Channel.CONFLATED ): Flow { @@ -215,7 +215,7 @@ fun DocumentReference.toFlow( * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all */ -fun Query.toFlow( +fun Query.snapshots( metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, bufferCapacity: Int? = Channel.CONFLATED ): Flow { From bcb69e572b208ef757e2924d49f7a3c63614207e Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Tue, 9 Aug 2022 20:27:57 +0200 Subject: [PATCH 17/23] remove wrong BuildConfig --- .../main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index b02639683d3..c9f3e652212 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -14,7 +14,6 @@ package com.google.firebase.firestore.ktx -import android.support.multidex.BuildConfig import androidx.annotation.Keep import com.google.firebase.FirebaseApp import com.google.firebase.components.Component From f6e1aebb9f2d980fd77759fcdcd6e3d4a2bcfb6b Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Wed, 10 Aug 2022 11:42:10 +0200 Subject: [PATCH 18/23] add play-services-basement to ktx --- firebase-firestore/firebase-firestore.gradle | 2 +- firebase-firestore/ktx/ktx.gradle | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/firebase-firestore/firebase-firestore.gradle b/firebase-firestore/firebase-firestore.gradle index ef781dce236..9046ae66cb4 100644 --- a/firebase-firestore/firebase-firestore.gradle +++ b/firebase-firestore/firebase-firestore.gradle @@ -144,7 +144,7 @@ dependencies { implementation "io.grpc:grpc-protobuf-lite:$grpcVersion" implementation "io.grpc:grpc-okhttp:$grpcVersion" implementation "io.grpc:grpc-android:$grpcVersion" - api 'com.google.android.gms:play-services-basement:18.0.0' + implementation 'com.google.android.gms:play-services-basement:18.0.0' implementation 'com.google.android.gms:play-services-tasks:18.0.1' implementation 'com.google.android.gms:play-services-base:18.0.1' diff --git a/firebase-firestore/ktx/ktx.gradle b/firebase-firestore/ktx/ktx.gradle index dca5246703a..16338af24e5 100644 --- a/firebase-firestore/ktx/ktx.gradle +++ b/firebase-firestore/ktx/ktx.gradle @@ -56,6 +56,7 @@ dependencies { implementation project(':firebase-firestore') implementation 'androidx.annotation:annotation:1.1.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' + implementation 'com.google.android.gms:play-services-basement:18.0.0' testImplementation project(':firebase-database-collection') testImplementation 'org.mockito:mockito-core:2.25.0' testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8' From fbd486425d6963b1272f9d04733d88c06bbdf0b0 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Wed, 10 Aug 2022 11:51:33 +0200 Subject: [PATCH 19/23] Update firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rosário Pereira Fernandes --- .../com/google/firebase/firestore/ktx/Firestore.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index c9f3e652212..2dc5f42f50a 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -205,11 +205,14 @@ fun DocumentReference.snapshots( } /** - * Transforms a [Query] into a coroutine [Flow] + * Starts listening to this query with the given options and emits its values via a [Flow]. * - * **Backpressure handling**: by default this method conflates items. If the consumer isn't fast enough, - * it might miss some values but is always guaranteed to get the latest value. Use [bufferCapacity] - * to change that behaviour + * - When the returned flow starts being collected, an [EventListener] will be attached. + * - When the flow completes, the listener will be removed. + * + * Backpressure: by default the returned flow is conflated. If the consumer isn't fast enough, + * it might miss some values but is always guaranteed to get the latest value. Use the [bufferCapacity] parameter + * to change that behaviour. * * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all From 31d3b6146ce31486f89e8f1525c47347a4147c39 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Wed, 10 Aug 2022 11:51:45 +0200 Subject: [PATCH 20/23] Update firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rosário Pereira Fernandes --- .../com/google/firebase/firestore/ktx/Firestore.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 2dc5f42f50a..99090da7e68 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -173,11 +173,14 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { } /** - * Transforms a [DocumentReference] into a coroutine [Flow] + * Starts listening to the document referenced by this `DocumentReference` with the given options and emits its values via a [Flow]. * - * **Backpressure handling**: by default this method conflates items. If the consumer isn't fast enough, - * it might miss some values but is always guaranteed to get the latest value. Use [bufferCapacity] - * to change that behaviour + * - When the returned flow starts being collected, an [EventListener] will be attached. + * - When the flow completes, the listener will be removed. + * + * Backpressure: by default the returned flow is conflated. If the consumer isn't fast enough, + * it might miss some values but is always guaranteed to get the latest value. Use the [bufferCapacity] parameter + * to change that behaviour. * * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all From 8cd52cdb216a6fc8345863ec8070a9b81d450c2b Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Wed, 10 Aug 2022 19:06:12 +0200 Subject: [PATCH 21/23] let the user decide the buffering --- firebase-firestore/ktx/api.txt | 4 +- .../firebase/firestore/ktx/Firestore.kt | 50 ++++--------------- 2 files changed, 11 insertions(+), 43 deletions(-) diff --git a/firebase-firestore/ktx/api.txt b/firebase-firestore/ktx/api.txt index 4e8c7b10668..cc32defb182 100644 --- a/firebase-firestore/ktx/api.txt +++ b/firebase-firestore/ktx/api.txt @@ -10,8 +10,8 @@ package com.google.firebase.firestore.ktx { method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath); method @Nullable public static inline T getField(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.FieldPath fieldPath, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static com.google.firebase.firestore.FirebaseFirestore getFirestore(@NonNull com.google.firebase.ktx.Firebase); - method @NonNull public static kotlinx.coroutines.flow.Flow snapshots(@NonNull com.google.firebase.firestore.DocumentReference, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE, @Nullable Integer bufferCapacity = -1); - method @NonNull public static kotlinx.coroutines.flow.Flow snapshots(@NonNull com.google.firebase.firestore.Query, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE, @Nullable Integer bufferCapacity = -1); + method @NonNull public static kotlinx.coroutines.flow.Flow snapshots(@NonNull com.google.firebase.firestore.DocumentReference, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE); + method @NonNull public static kotlinx.coroutines.flow.Flow snapshots(@NonNull com.google.firebase.firestore.Query, @NonNull com.google.firebase.firestore.MetadataChanges metadataChanges = com.google.firebase.firestore.MetadataChanges.EXCLUDE); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot); method @Nullable public static inline T toObject(@NonNull com.google.firebase.firestore.DocumentSnapshot, @NonNull com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior serverTimestampBehavior); method @NonNull public static inline T toObject(@NonNull com.google.firebase.firestore.QueryDocumentSnapshot); diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 99090da7e68..799615546ef 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -18,22 +18,14 @@ import androidx.annotation.Keep import com.google.firebase.FirebaseApp import com.google.firebase.components.Component import com.google.firebase.components.ComponentRegistrar -import com.google.firebase.firestore.DocumentReference -import com.google.firebase.firestore.DocumentSnapshot -import com.google.firebase.firestore.FieldPath -import com.google.firebase.firestore.FirebaseFirestore -import com.google.firebase.firestore.FirebaseFirestoreSettings -import com.google.firebase.firestore.MetadataChanges -import com.google.firebase.firestore.Query -import com.google.firebase.firestore.QueryDocumentSnapshot -import com.google.firebase.firestore.QuerySnapshot +import com.google.firebase.firestore.* +import com.google.firebase.firestore.util.Executors.BACKGROUND_EXECUTOR import com.google.firebase.ktx.Firebase import com.google.firebase.platforminfo.LibraryVersionComponent import kotlinx.coroutines.cancel -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow /** Returns the [FirebaseFirestore] instance of the default [FirebaseApp]. */ @@ -178,33 +170,21 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { * - When the returned flow starts being collected, an [EventListener] will be attached. * - When the flow completes, the listener will be removed. * - * Backpressure: by default the returned flow is conflated. If the consumer isn't fast enough, - * it might miss some values but is always guaranteed to get the latest value. Use the [bufferCapacity] parameter - * to change that behaviour. - * * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] - * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all */ fun DocumentReference.snapshots( metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, - bufferCapacity: Int? = Channel.CONFLATED ): Flow { - val flow = callbackFlow { - val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> + return callbackFlow { + val registration = addSnapshotListener(BACKGROUND_EXECUTOR, metadataChanges) { snapshot, exception -> if (exception != null) { cancel(message = "Error getting DocumentReference snapshot", cause = exception) } else if (snapshot != null) { - trySend(snapshot) + trySendBlocking(snapshot) } } awaitClose { registration.remove() } } - - return if (bufferCapacity != null) { - flow.buffer(bufferCapacity) - } else { - flow - } } /** @@ -213,31 +193,19 @@ fun DocumentReference.snapshots( * - When the returned flow starts being collected, an [EventListener] will be attached. * - When the flow completes, the listener will be removed. * - * Backpressure: by default the returned flow is conflated. If the consumer isn't fast enough, - * it might miss some values but is always guaranteed to get the latest value. Use the [bufferCapacity] parameter - * to change that behaviour. - * * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] - * @param bufferCapacity the buffer capacity as in [Flow.buffer] or null to not buffer at all */ fun Query.snapshots( metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, - bufferCapacity: Int? = Channel.CONFLATED ): Flow { - val flow = callbackFlow { - val registration = addSnapshotListener(metadataChanges) { snapshot, exception -> + 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) { - trySend(snapshot) + trySendBlocking(snapshot) } } awaitClose { registration.remove() } } - - return if (bufferCapacity != null) { - flow.buffer(bufferCapacity) - } else { - flow - } } From 253fbcaba88080d27cced485bcb063668d162624 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 11 Aug 2022 15:01:57 +0200 Subject: [PATCH 22/23] remove wildcard import --- .../com/google/firebase/firestore/ktx/Firestore.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 799615546ef..5ffaa7a504d 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -18,7 +18,15 @@ import androidx.annotation.Keep import com.google.firebase.FirebaseApp import com.google.firebase.components.Component import com.google.firebase.components.ComponentRegistrar -import com.google.firebase.firestore.* +import com.google.firebase.firestore.DocumentSnapshot +import com.google.firebase.firestore.FieldPath +import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.firestore.FirebaseFirestoreSettings +import com.google.firebase.firestore.QueryDocumentSnapshot +import com.google.firebase.firestore.QuerySnapshot +import com.google.firebase.firestore.DocumentReference +import com.google.firebase.firestore.Query +import com.google.firebase.firestore.MetadataChanges import com.google.firebase.firestore.util.Executors.BACKGROUND_EXECUTOR import com.google.firebase.ktx.Firebase import com.google.firebase.platforminfo.LibraryVersionComponent From 692114cc815a109667461a817d501ed7872bd3d5 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 11 Aug 2022 15:17:10 +0200 Subject: [PATCH 23/23] make lint happy --- .../com/google/firebase/firestore/ktx/Firestore.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt index 5ffaa7a504d..5c5867856d6 100644 --- a/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt +++ b/firebase-firestore/ktx/src/main/kotlin/com/google/firebase/firestore/ktx/Firestore.kt @@ -18,15 +18,15 @@ import androidx.annotation.Keep import com.google.firebase.FirebaseApp import com.google.firebase.components.Component import com.google.firebase.components.ComponentRegistrar +import com.google.firebase.firestore.DocumentReference import com.google.firebase.firestore.DocumentSnapshot import com.google.firebase.firestore.FieldPath import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.FirebaseFirestoreSettings +import com.google.firebase.firestore.MetadataChanges +import com.google.firebase.firestore.Query import com.google.firebase.firestore.QueryDocumentSnapshot import com.google.firebase.firestore.QuerySnapshot -import com.google.firebase.firestore.DocumentReference -import com.google.firebase.firestore.Query -import com.google.firebase.firestore.MetadataChanges import com.google.firebase.firestore.util.Executors.BACKGROUND_EXECUTOR import com.google.firebase.ktx.Firebase import com.google.firebase.platforminfo.LibraryVersionComponent @@ -181,7 +181,7 @@ class FirebaseFirestoreKtxRegistrar : ComponentRegistrar { * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] */ fun DocumentReference.snapshots( - metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, + metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE ): Flow { return callbackFlow { val registration = addSnapshotListener(BACKGROUND_EXECUTOR, metadataChanges) { snapshot, exception -> @@ -204,7 +204,7 @@ fun DocumentReference.snapshots( * @param metadataChanges controls metadata-only changes. Default: [MetadataChanges.EXCLUDE] */ fun Query.snapshots( - metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE, + metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE ): Flow { return callbackFlow { val registration = addSnapshotListener(BACKGROUND_EXECUTOR, metadataChanges) { snapshot, exception ->