@@ -8,12 +8,16 @@ import com.apollographql.apollo.api.Response
8
8
import com.apollographql.apollo.exception.ApolloException
9
9
import kotlinx.coroutines.*
10
10
import kotlinx.coroutines.channels.Channel
11
- import kotlinx.coroutines.flow.flow
11
+ import kotlinx.coroutines.channels.awaitClose
12
+ import kotlinx.coroutines.flow.*
12
13
13
14
private class ChannelCallback <T >(val channel : Channel <Response <T >>) : ApolloCall.Callback<T>() {
14
15
16
+ @ExperimentalCoroutinesApi
15
17
override fun onResponse (response : Response <T >) {
16
- channel.offer(response)
18
+ if (! channel.isClosedForSend) {
19
+ channel.offer(response)
20
+ }
17
21
}
18
22
19
23
override fun onFailure (e : ApolloException ) {
@@ -39,64 +43,72 @@ private fun checkCapacity(capacity: Int) {
39
43
}
40
44
41
45
/* *
42
- * Converts an {@link ApolloCall} to an {@link kotlinx.coroutines.flow. Flow} .
46
+ * Converts an [ ApolloCall] to an [ Flow] .
43
47
*
44
48
* @param <T> the value type.
45
- * @param capacity the {@link Capacity} used for the underlying channel. Only {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
46
- * and {@link kotlinx.coroutines.channels.Channel.CONFLATED} are supported at the moment
47
- * @throws IllegalArgumentException if capacity is not {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
48
- * or {@link kotlinx.coroutines.channels.Channel.CONFLATED}
49
- * @return a flow which emits Responses<T>
49
+ * @return a flow which emits [Responses<T>]
50
50
*/
51
- fun <T > ApolloCall<T>.toFlow (capacity : Int = Channel .UNLIMITED ) = flow {
52
- checkCapacity(capacity)
53
- val channel = Channel <Response <T >>(capacity)
54
-
55
- enqueue(ChannelCallback (channel = channel))
56
- try {
57
- for (item in channel) {
58
- emit(item)
51
+ @ExperimentalCoroutinesApi
52
+ fun <T > ApolloCall<T>.toFlow () = callbackFlow {
53
+ clone().enqueue(
54
+ object : ApolloCall .Callback <T >() {
55
+ override fun onResponse (response : Response <T >) {
56
+ offer(response)
59
57
}
60
- } finally {
61
- cancel()
62
- }
58
+
59
+ override fun onFailure (e : ApolloException ) {
60
+ close(e)
61
+ }
62
+
63
+ override fun onStatusEvent (event : ApolloCall .StatusEvent ) {
64
+ if (event == ApolloCall .StatusEvent .COMPLETED ) {
65
+ close()
66
+ }
67
+ }
68
+ }
69
+ )
70
+ awaitClose { this @toFlow.cancel() }
63
71
}
64
72
65
73
/* *
66
- * Converts an {@link ApolloQueryWatcher} to an {@link kotlinx.coroutines.flow. Flow} .
74
+ * Converts an [ ApolloQueryWatcher] to an [ Flow] .
67
75
*
68
76
* @param <T> the value type.
69
- * @param capacity the {@link Capacity} used for the underlying channel. Only {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
70
- * and {@link kotlinx.coroutines.channels.Channel.CONFLATED} are supported at the moment
71
- * @throws IllegalArgumentException if capacity is not {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
72
- * or {@link kotlinx.coroutines.channels.Channel.CONFLATED}
73
- * @return a flow which emits Responses<T>
77
+ * @return a flow which emits [Responses<T>]
74
78
*/
75
- fun <T > ApolloQueryWatcher<T>.toFlow (capacity : Int = Channel .UNLIMITED ) = flow {
76
- checkCapacity(capacity)
77
- val channel = Channel <Response <T >>(capacity)
78
-
79
- enqueueAndWatch(ChannelCallback (channel = channel))
80
- try {
81
- for (item in channel) {
82
- emit(item)
79
+ @ExperimentalCoroutinesApi
80
+ fun <T > ApolloQueryWatcher<T>.toFlow () = callbackFlow {
81
+ clone().enqueueAndWatch(
82
+ object : ApolloCall .Callback <T >() {
83
+ override fun onResponse (response : Response <T >) {
84
+ offer(response)
83
85
}
84
- } finally {
85
- cancel()
86
- }
86
+
87
+ override fun onFailure (e : ApolloException ) {
88
+ close(e)
89
+ }
90
+
91
+ override fun onStatusEvent (event : ApolloCall .StatusEvent ) {
92
+ if (event == ApolloCall .StatusEvent .COMPLETED ) {
93
+ close()
94
+ }
95
+ }
96
+ }
97
+ )
98
+ awaitClose { this @toFlow.cancel() }
87
99
}
88
100
89
101
/* *
90
- * Converts an {@link ApolloCall} to an {@link kotlinx.coroutines.channels. Channel} . The number of values produced
91
- * by the channel is based on the {@link com.apollographql.apollo.fetcher.ResponseFetcher} used with the call.
102
+ * Converts an [ ApolloCall] to an [ Channel] . The number of values produced by the channel is based on the
103
+ * [ com.apollographql.apollo.fetcher.ResponseFetcher] used with the call.
92
104
*
93
105
* @param <T> the value type.
94
- * @param capacity the {@link Capacity} used for the underlying channel. Only {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
95
- * and {@link kotlinx.coroutines.channels.Channel.CONFLATED} are supported at the moment
96
- * @throws IllegalArgumentException if capacity is not {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
97
- * or {@link kotlinx.coroutines.channels.Channel.CONFLATED}
98
- * @return the converted channel
106
+ * @param capacity used for the underlying channel. Only [Channel.UNLIMITED] and [Channel.CONFLATED] are supported at the moment
107
+ * @throws IllegalArgumentException if capacity is not [Channel.UNLIMITED] or [Channel.CONFLATED]
108
+ * @return a channel which emits [Responses<T>]
99
109
*/
110
+ @ExperimentalCoroutinesApi
111
+ @Deprecated(message = " Use toFlow instead" , replaceWith = ReplaceWith (" toFlow()" ))
100
112
fun <T > ApolloCall<T>.toChannel (capacity : Int = Channel .UNLIMITED ): Channel <Response <T >> {
101
113
checkCapacity(capacity)
102
114
val channel = Channel <Response <T >>(capacity)
@@ -110,9 +122,8 @@ fun <T> ApolloCall<T>.toChannel(capacity: Int = Channel.UNLIMITED): Channel<Resp
110
122
}
111
123
112
124
/* *
113
- * Converts an {@link ApolloCall} to an {@link kotlinx.coroutines.Deferred}. This is a convenience method
114
- * that will only return the first value emitted. If the more than one response is required, for an example
115
- * to retrieve cached and network response, use {@link toChannel} instead.
125
+ * Converts an [ApolloCall] to an [Deferred]. This is a convenience method that will only return the first value emitted.
126
+ * If the more than one response is required, for an example to retrieve cached and network response, use [toChannel] instead.
116
127
*
117
128
* @param <T> the value type.
118
129
* @return the deferred
@@ -127,31 +138,34 @@ fun <T> ApolloCall<T>.toDeferred(): Deferred<Response<T>> {
127
138
}
128
139
enqueue(object : ApolloCall .Callback <T >() {
129
140
override fun onResponse (response : Response <T >) {
130
- deferred.complete(response)
141
+ if (deferred.isActive) {
142
+ deferred.complete(response)
143
+ }
131
144
}
132
145
133
146
override fun onFailure (e : ApolloException ) {
134
- deferred.completeExceptionally(e)
147
+ if (deferred.isActive) {
148
+ deferred.completeExceptionally(e)
149
+ }
135
150
}
136
151
})
137
152
138
153
return deferred
139
154
}
140
155
141
156
/* *
142
- * Converts an {@link ApolloQueryWatcher} to an {@link kotlinx.coroutines.channels. Channel} .
157
+ * Converts an [ ApolloQueryWatcher] to an [ Channel] .
143
158
*
144
159
* @param <T> the value type.
145
- * @param capacity the {@link Capacity} used for the underlying channel. Only {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
146
- * and {@link kotlinx.coroutines.channels.Channel.CONFLATED} are supported at the moment
147
- * @throws IllegalArgumentException if capacity is not {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
148
- * or {@link kotlinx.coroutines.channels.Channel.CONFLATED}
149
- * @return the converted channel
160
+ * @param capacity used for the underlying channel. Only [Channel.UNLIMITED] and [Channel.CONFLATED] are supported at the moment
161
+ * @throws IllegalArgumentException if capacity is not [Channel.UNLIMITED] or [Channel.CONFLATED]
162
+ * @return a channel which emits [Responses<T>]
150
163
*/
164
+ @ExperimentalCoroutinesApi
165
+ @Deprecated(message = " Use toFlow instead" , replaceWith = ReplaceWith (" toFlow()" ))
151
166
fun <T > ApolloQueryWatcher<T>.toChannel (capacity : Int = Channel .UNLIMITED ): Channel <Response <T >> {
152
167
checkCapacity(capacity)
153
168
val channel = Channel <Response <T >>(capacity)
154
-
155
169
channel.invokeOnClose {
156
170
cancel()
157
171
}
@@ -161,19 +175,19 @@ fun <T> ApolloQueryWatcher<T>.toChannel(capacity: Int = Channel.UNLIMITED): Chan
161
175
}
162
176
163
177
/* *
164
- * Converts an {@link ApolloSubscriptionCall} to an {@link kotlinx.coroutines.channels. Channel} .
178
+ * Converts an [ ApolloSubscriptionCall] to an [ Channel] .
165
179
*
166
180
* @param <T> the value type.
167
- * @param capacity the {@link Capacity} used for the underlying channel. Only {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
168
- * and {@link kotlinx.coroutines.channels.Channel.CONFLATED} are supported at the moment
169
- * @throws IllegalArgumentException if capacity is not {@link kotlinx.coroutines.channels.Channel.UNLIMITED}
170
- * or {@link kotlinx.coroutines.channels.Channel.CONFLATED}
171
- * @return the converted channel
181
+ * @param capacity the {@link Capacity} used for the underlying channel. Only [Channel.UNLIMITED] and [Channel.CONFLATED] are supported
182
+ * at the moment
183
+ * @throws IllegalArgumentException if capacity is not [Channel.UNLIMITED] or [Channel.CONFLATED]
184
+ * @return a channel which emits [Responses<T>]
172
185
*/
186
+ @ExperimentalCoroutinesApi
187
+ @Deprecated(message = " Use toFlow instead" , replaceWith = ReplaceWith (" toFlow()" ))
173
188
fun <T > ApolloSubscriptionCall<T>.toChannel (capacity : Int = Channel .UNLIMITED ): Channel <Response <T >> {
174
189
checkCapacity(capacity)
175
190
val channel = Channel <Response <T >>(capacity)
176
-
177
191
channel.invokeOnClose {
178
192
cancel()
179
193
}
@@ -202,7 +216,40 @@ fun <T> ApolloSubscriptionCall<T>.toChannel(capacity: Int = Channel.UNLIMITED):
202
216
}
203
217
204
218
/* *
205
- * Converts an {@link ApolloPrefetch} to an {@link kotlinx.coroutines.Job}.
219
+ * Converts an [ApolloSubscriptionCall] to an [Flow].
220
+ *
221
+ * @param <T> the value type.
222
+ * @return a flow which emits [Responses<T>]
223
+ */
224
+ @ExperimentalCoroutinesApi
225
+ fun <T > ApolloSubscriptionCall<T>.toFlow (): Flow <Response <T >> = callbackFlow {
226
+ clone().execute(
227
+ object : ApolloSubscriptionCall .Callback <T > {
228
+ override fun onConnected () {
229
+ }
230
+
231
+ override fun onResponse (response : Response <T >) {
232
+ channel.offer(response)
233
+ }
234
+
235
+ override fun onFailure (e : ApolloException ) {
236
+ channel.close(e)
237
+ }
238
+
239
+ override fun onCompleted () {
240
+ channel.close()
241
+ }
242
+
243
+ override fun onTerminated () {
244
+ channel.close()
245
+ }
246
+ }
247
+ )
248
+ awaitClose { this @toFlow.cancel() }
249
+ }
250
+
251
+ /* *
252
+ * Converts an [ApolloPrefetch] to [Job].
206
253
*
207
254
* @param <T> the value type.
208
255
* @return the converted job
@@ -218,11 +265,15 @@ fun ApolloPrefetch.toJob(): Job {
218
265
219
266
enqueue(object : ApolloPrefetch .Callback () {
220
267
override fun onSuccess () {
221
- deferred.complete(Unit )
268
+ if (deferred.isActive) {
269
+ deferred.complete(Unit )
270
+ }
222
271
}
223
272
224
273
override fun onFailure (e : ApolloException ) {
225
- deferred.completeExceptionally(e)
274
+ if (deferred.isActive) {
275
+ deferred.completeExceptionally(e)
276
+ }
226
277
}
227
278
})
228
279
0 commit comments