Hi everyone :wave::skin-tone-3: TLDR: I want to c...
# coroutines
b
Hi everyone 👋🏼 TLDR: I want to create extension functions for Flows, without needing to pass in dependencies as parameters. I've been trying to see if there is a way for me to create an extension function for a Flow, that doesn't require me to pass in the dependency of the internal functionality.
Copy code
//pseudo code

flowOf(1,2,3)
   .onStartCheckAndThrowNoInternetException()
   .map { it + 1 }
   .catchNoInternetException()
   .catch { //catch general error }
   .collect()
The above code is what I am hoping to achieve, but the functionality to do the check requires a dependency. Or more dependencies depending on exactly what I want to do. The point is that I want to be able to provide these extension function from a constructed class, where all logic is internal to the class, instead of having to provide all the dependencies here.
I have gotten a little way in doing this, but based on available documentation, I will face unsafe thread issues. Here is an example of a small FlowCollector potential:
Copy code
class CustomFlowCollector<T> @Inject constructor(
    private val internetConnection: InternetConnection
) : FlowCollector<T> {
    fun action() {
        if (!internetConnection.checkConnection()) throw NoInternetConnectionException()
    }

    override suspend fun emit(value: T) = Unit

    companion object {
        fun <T> Flow<T>.onStartCheckAndThrowNoInternetException(
            customFlowCollector: CustomFlowCollector<T>
        ): Flow<T> = onStart {
            customFlowCollector.action()
        }
    }
}
Here's the usage for the above:
Copy code
//pseudo code

@Inject
lateinit var customFlowCollector: CustomFlowCollector

flowOf(1,2,3)
   .onStartCheckAndThrowNoInternetException(customFlowCollector)
   .map { it + 1 }
   .catchNoInternetException()
   .catch { //catch general error }
   .collect()
y
Copy code
class MyConstructedClass {
  fun <T> Flow<T>.onStartCheckBlah(): Flow<T>
}
// usage
with(MyConstructedClass()) {
  flowOf(...)
    .onStartCheckBlah()
    .blah
}
Or even with your idea:
Copy code
class CustomFlowCollector<T> @Inject constructor(
    private val internetConnection: InternetConnection
) {
    companion object {
        fun <T> Flow<T>.onStartCheckAndThrowNoInternetException(
            customFlowCollector: CustomFlowCollector<T>
        ): Flow<T> = onStart {
            if (!customFlowCollector.internetConnection.checkConnection()) throw NoInternetConnectionException()
        }
    }
}
b
Thank you for the input @Youssef Shoaib [MOD]. I would rather not have to wrap any flow in a
with
, but maybe I use that in some other way. Any other suggestions are also welcome. 🙏🏼
y
The second solution is just your idea but without implementing
FlowCollector
or any such "dangerous" APIs. It's just a good-old extension method. Its usage is exactly as you provided:
Copy code
@Inject
lateinit var customFlowCollector: CustomFlowCollector

flowOf(1,2,3)
   .onStartCheckAndThrowNoInternetException(customFlowCollector)
   .map { it + 1 }
   .catchNoInternetException()
   .catch { //catch general error }
   .collect()
There's nothing bad about surrounding a
flowOf
with a
with
btw. A
with
just brings an object in as a receiver, so that it may be used to e.g. call member extension functions (in my first solution,
onStartCheckBlah
is a member of
MyCustomClass
, but it's an extension of
Flow
, and so it needs
MyCustomClass
in scope, while receiving
Flow
either in scope or explicitly using the
.
). Context parameters are also likely to help you here.
🙏🏼 1
s
If I've understood correctly, perhaps you could have the injected class extend from
(Flow<T>) -> Flow<T>
and then just apply it to the flow with
let
.
Copy code
flowOf(1,2,3).let(myHandler).etc()
2