ReactiveCircus / FlowBinding

Kotlin Coroutines Flow binding APIs for Android's platform and unbundled UI widgets, inspired by RxBinding.
https://reactivecircus.github.io/FlowBinding/
Apache License 2.0
899 stars 42 forks source link

Suggestion: support Lifecycle events #70

Closed svenjacobs closed 4 years ago

svenjacobs commented 4 years ago

Thanks for this useful library πŸ‘

I suggest adding a new artifact flowbinding-lifecycle which supports Flow of Lifecycle events. Here's a proposal of how this could look like:

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Lifecycle.Event
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map

fun Lifecycle.events(): Flow<Event> = callbackFlow {
    val observer = object : LifecycleObserver {
        @OnLifecycleEvent(Event.ON_ANY)
        fun onEvent(owner: LifecycleOwner, event: Event) {
            offer(event)
        }
    }
    addObserver(observer)
    awaitClose { removeObserver(observer) }
}

fun Lifecycle.onCreate(): Flow<Unit> =
    events()
        .filter { it == Event.ON_CREATE }
        .map { Unit }

Thank you!

ychescale9 commented 4 years ago

Hi. Thanks for the feature request!

Can you give me a couple of use cases i.e. what would you do with a Flow of LifecycleEvents?

svenjacobs commented 4 years ago

Can you give me a couple of use cases i.e. what would you do with a Flow of LifecycleEvents?

I'm currently implementing the Model-View-Intent pattern in an Android application utilizing coroutines and Flow. My idea is that any kind of event, be it a click event or a lifecycle event, is represented through Flows. So the only communication between the view and the "presenter" is through Flow.

interface SomeView {
  val onCreateEvents: Flow<Unit>
  val onButtonClickEvents: Flow<Unit>
}

then somewhere else

   val onCreateFlow = view.onCreateEvents
    .onEach { // Do something with this event }

   val onButtonClickFlow = view.onButtonClickEvents
    .onEach { // Do something with this event }

  flowOf(
    onCreateFlow,
    onButtonClickFlow
  ).flattenMerge().launchIn(someScope)

The example is greatly simplified but I hope you get what I mean.

ychescale9 commented 4 years ago
val onCreateFlow = view.onCreateEvents
  .onEach { // Do something with this event }

What would you put in the onEach block here? πŸ˜„Is it some kind of side-effect or mapping to some sealed Event type that the presenter / view model takes as input?

I'm asking as I can't quite wrap my head around how a stream of lifecycle events can help in a MVI setup. Ideally the presenter / viewmodel should not be aware of the Android Lifecycle. The only use case I can think of where monitoring Lifecycle events is useful (other than subscribing and unsubscribing event listeners) is analytics reporting. Am I missing something?

Anyway this library probably shouldn't be too opinionated on how it's used. So I'll work on this next week.

svenjacobs commented 4 years ago

What would you put in the onEach block here? πŸ˜„Is it some kind of side-effect or mapping to some sealed Event type that the presenter / view model takes as input?

As I said my example is simplified πŸ˜‰ What I do is (instead of onEach) map the incoming events into changes of a State data class. This State represents the UI state. For the onCreate event for example I would start loading some data and put it in the state.

ychescale9 commented 4 years ago

Got you πŸ‘

svenjacobs commented 4 years ago

Nice πŸ˜„ I'm looking forward to your implementation!

ychescale9 commented 4 years ago

Added new LifecycleEventFlow. This will be in the next release.

svenjacobs commented 4 years ago

Nice, thanks! πŸ‘

ychescale9 commented 4 years ago

0.9.0 is out. Should be available on Maven Central soon.