https://kotlinlang.org logo
#codereview
Title
# codereview
m

Mark

08/30/2020, 2:33 AM
Is there a better way to do this?
Copy code
class FlowDelegate<T>: ReadWriteProperty<Any, Flow<T>> {
    private val deferredFlow = CompletableDeferred<Flow<T>>()

    override fun setValue(thisRef: Any, property: KProperty<*>, value: Flow<T>) {
        deferredFlow.complete(value)
    }

    override fun getValue(thisRef: Any, property: KProperty<*>): Flow<T> {
        return if (deferredFlow.isCompleted) {
            deferredFlow.getCompleted()
        } else {
            flow {
                emitAll(deferredFlow.await())
            }
        }
    }
}

/** Usage */
class MySample {
    private var _sampleFlow by FlowDelegate<String>()

    val sampleFlow: Flow<String>
        get() = _sampleFlow

    fun init(args: ...) {
        _sampleFlow = flow<String> { /* based on args */ }
    }
}
z

Zach Klippenstein (he/him) [MOD]

08/30/2020, 2:01 PM
I would think you would want to throw if you try setting the property more than once, since that isn't supported.
m

Mark

08/30/2020, 3:51 PM
Good point, thanks! I was expecting something like this in the coroutines library, because it seems like a quite common use case.
z

Zach Klippenstein (he/him) [MOD]

08/30/2020, 3:54 PM
I don’t know your actual use case, but using
Deferred<Flow<>>
directly for this seems more clear/less magical, and isn’t really that much boilerplate.
m

Mark

08/30/2020, 4:10 PM
But then won’t you need a suspend function to access the flow object?
z

Zach Klippenstein (he/him) [MOD]

08/30/2020, 6:39 PM
No, you could do the same trick you are where you wrap it in a flow. Can't remember if there's a Deferred.asFlow function but you could chain that with flatten to get the same thing.
m

Mark

08/31/2020, 3:53 AM
Maybe something like this instead (this time allowing init() to be called multiple times)?
Copy code
class MySample {
    private val _sampleFlowChannel = ConflatedBroadcastChannel<Flow<String>>()

    val sampleFlow: Flow<String>
        get() = _sampleFlowChannel.asFlow().flattenConcat()

    fun init(args: ...) {
        val f = flow<String> { /* based on args */ }
        _sampleFlowChannel.offer(f)
    }
}
👍 1
m

myanmarking

12/08/2020, 3:05 PM
why flow {emitAll … and not flowOf(deferredFlow.await) ?
m

Mark

12/09/2020, 5:06 AM
@myanmarking Because that requires a suspend function.
m

myanmarking

12/09/2020, 12:13 PM
yes, right
on the other hand, i think it’s safe to say emitAll should be emit. right ? Actually, i’m not sure now 😛
3 Views