Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a trigger parameter to delay feature start. #89

Closed
ShikaSD opened this issue Jun 24, 2019 · 4 comments
Closed

Add a trigger parameter to delay feature start. #89

ShikaSD opened this issue Jun 24, 2019 · 4 comments
Labels
enhancement New feature or request
Milestone

Comments

@ShikaSD
Copy link
Contributor

ShikaSD commented Jun 24, 2019

Current implementation of the feature launches internal subscriptions when created. Although it is conceptually correct, the limitations of the Android framework sometimes force us to wait until onCreate to have any meaningful data.
This proposal suggests to add an optional startWhen parameter to the feature constructor, so its start can be done after creation.

Suggested update to the feature constructor:

class BaseFeature(
    initialState: State,
    ...,
    startWhen: Single<Unit> = Single.just(Unit)
)
@ShikaSD ShikaSD changed the title Add a trigger parameter to delay bootstrapper subscription and state emission. Add a trigger parameter to delay feature start. Jun 24, 2019
@ShikaSD ShikaSD mentioned this issue Jun 24, 2019
8 tasks
@zsoltk zsoltk added the enhancement New feature or request label Jun 24, 2019
@zsoltk zsoltk added this to the 2.0 milestone Jun 24, 2019
@BraisGabin
Copy link

We were having two different problems for this issue:

  1. Problems for test the feature itself

This one is easier to explain: We were unit testing the parts of the feuater: actor, reducer, etc. But then we saw a PR where we were just changing the initial state. And there was no need to change any tests. That was a code smell. We weren't testing that.

We thought to test the feature as a blackbox. The actor and reducer are just an "implementation detail". We want to check that if we send the wish X we receive the State Y. This way moving from MVICore to any other MVI framework should be easier. It seems a good way.

The problem is that we can't test correctly the initial state because the bootstraper runs as soon as we create the feature so we never receive that initial state. I know that there are ways to avoid this. I'm just showing why this is a problem.

  1. Leaks

If we have this feature:

class Asdf : BaseFeature<Unit, Unit, Unit, Unit, Unit>(
  initialState = Unit,
  bootstrapper = { Observable.just(Unit) },
  wishToAction = {},
  actor = { _, _ ->
    Observable.never<Unit>()
      .doOnSubscribe { Log.d("MVICore", "doOnSubscribe") }
      .doOnDispose { Log.d("MVICore", "doOnDispose") }
  },
  reducer = { _, _ -> Unit })

We know that as soon as I run Asdf() I will receive doOnSubscribe.

In our code, we have a much complex feature and just for creating it we were leaking all the Activity. This was because, some times, we weren't subscribing to the Feature se we thought it was pointless to dispose it.


My 2 cents: The feature should not launch any internal subscription when created. The creation of an object should not have any side effect. So, instead of adding the startWhen, start when someone subscribes to the feature.

@zsoltk
Copy link
Contributor

zsoltk commented Aug 10, 2019

This again seems to stem from a confusion about what a Feature is. What you describe as "start when someone subscribes to the feature" sounds like a cold observable. A Feature is a hot one. It's push-based, and not pull-based: it's not driven or scoped by subscribers to it. It just "is", does its job, and if you want, you can connect to it for some time. This is what allows it to run in the background, listen to server, and be up-to-date with the latest state regardless whether there's a UI connected to it or not. The documentation tries to make a clear point to always call .dispose() on it when its lifecycle ends. Maybe we should stress the hot observable part more.

@BraisGabin
Copy link

Ok, I agree that probably the subscription should not be the started of the feature (or at least not the only one). But I'm still don't get why to start the feature as soon as it's created.

With the @ShikaSD's proposal I could archive this but I'm not sure if start the subscription in the creation is the best "default behaviour". (I'm not sure if this seems kind of rude. It's not my intention. I just want to share another point of view)

@ShikaSD
Copy link
Contributor Author

ShikaSD commented Aug 11, 2019

I would argue that we don't need to add support from the library to achieve any of this (following our internal discussions).
You can do it yourself already using one of the following:

// 1. Pass source to feature, delay in bootstrapper manually
class ExampleFeature(
    val startSignal: ObservableSource<Unit>,
): BaseFeature(...) {
    class BootstrapperImpl(val startSignal: ObservableSource<Unit>): Bootstrapper<Action> {
        override fun invoke() =
            Observable.wrap(startSignal)
                .firstElement()
                .flatMapObservable { /* do your thing */ }
    }
}

// 2. Use wish to init subscriptions

class ExampleFeature: BaseFeature(...) {
    sealed class Wish {
        object Init: Wish()
    }
 
    class ActorImpl: Actor<State, Wish, Effect> {
        override fun invoke(state: State, wish: Wish) =
            when (wish) {
                is Init -> /* do your thing */
            }
    }
}

Answering @BraisGabin question:

why to start the feature as soon as it's created.

It is started because it is supposed to be a hot observable. Imagine it like glorified BehaviourSubject: you create it with createDefault, it emits its first state straight away and continues to emit whatever is put into it after creation. It doesn't wait for subscriptions or anything else.

@zsoltk zsoltk closed this as completed Jul 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants