Hello all, I want to convert the value returning f...
# coroutines
n
Hello all, I want to convert the value returning from a suspend function to Flow.
Copy code
val myFlow = flow {
    emit(myRepository.suspendFunctionReturnsAValue())
}
Is there any built-in way to do this better than that such as
myRepository.suspendFunctionReturnsAValue().asFlow()
s
I don’t think so, since
myRepository.suspendFunctionReturnsAValue()
just returns a plain value. You could do something like this instead:
suspend { myRepository.suspendFunctionReturnsAValue() }.asFlow()
and define the
asFlow
extension function yourself:
fun <T> (suspend () -> T).asFlow(): Flow<T> = flow { this() }
However, this seems more verbose than the one you try to avoid….
d
myRepository::suspendFunctionReturnsAValue.asFlow()
💯 3
1
s
^^^ It’s a bit trickier to use a method reference (
::
) if the
suspendFunctionReturnsAValue
is a function that takes one or more parameters….
l
Dominic Fischer answer is the best one, but do you really need to convert that suspending function to a single value
Flow
?
n
@Dominaezzz I also thought of that but @streetsofboston is right. We cannot use it if the function takes parameters. @louiscad I want to convert Retrofit suspend functions to Flow and combine them with other Retrofit or Room Flows with operators.
Or trying to find easy ways for migrating from RxJava.
I prepared something like that. Do you think is there any inappropriate issue with this?
d
I don't think
Flow
is the tool you need for this.
What about the good ol'
coroutineScope
and
async
?
n
While migrating from RxJava, I converted Retrofit `Single`s and `Completable`s to suspend functions. However, these `Single`s and `Completable`s combines with other `Observable`s with operators in repository. So I need a smooth way to do this.
A
Flow
adapter for Retrofit is also a method of course.
d
Hmm, you could always do
fun <T> singleFlow(block: suspend () -> T) = flow { emit(block()) }
then do
singleFlow { myRepository.suspendFunctionReturnsAValue() }
.
Converting a suspend function to a flow will always look a bit ...much, because it's not a stream.
p
I had a similar problem migrating retrofit-RxJava1 to retrofit-coroutine. A couple of people in this channel suggested refactor the code to use suspend but due to a CustomHttpException thrown in original code, which was attended all over, I went a different route. I end up doing an extension function on retrofit Call<T> class. Like bellow:
Copy code
fun <T> Call<T>.asFlow(): Flow<T> = flow {

	val resp = execute()

	if (resp.isSuccessful) {

		when( val respBody = resp.body() ) {

			null -> { throw HttpNullBodyException("Unexpected Null Body") }

			else -> {

				emit(respBody)

			}

		}

	} else {

		val errorInfo = resp.errorBody()?.string() ?: "errorBody empty"

		throw HttpCodeException(resp.code(), errorInfo)

	}

}
I first had tried Dominic approach but it forced me to place a boilerplate
catch()
operator after every retrofit call to map to the CustomHttpException in case of request failure. The same cause forced my
::retrofitFunct.asFlow()
to return a Flow<Resp<T>> and not a Flow<T> which is cleaner. Is worth mentioning that above custom exceptions extends from RuntimeException due to an issue with RxJava1 not propagating CheckedExceptions thrown from
onNext()
to
onError()
but rather crashing the app.
s
It depends on the needs of your code and app, but in general, try to avoid using a
Flow<T>
to just emit a single value of type
T
. Just use a
suspend someFunction(....): T
instead in that case. `Flow`s are like
suspend
functions, with respect to how they both handle Structured Concurrency and by the fact that they both produce (a) value(s) in a ‘cold’ way (‘cold’ vs ‘hot’ streams). Their difference is that a
Flow
produces multiple values, and a
suspend
function produces only one.