Skip to content

Delaying bootstrapping

Bootstrapping is immediate by default

As noted in the core concepts section, Features are essentially hot observables. It's worth to repeat this here, because there's an important aspect of it here: Bootstrapper is invoked immediately on Feature creation and by default will not wait for anything else.

You can picture the Feature as if it was a glorified BehaviourSubject: when you create that with createDefault, it emits its first state straight away and continues to emit whatever is put into it after creation.

Missing initial emissions?

Following from this, it is possible that by the time you subscribe to your Feature, the actions triggered by Bootstrapper have already resulted in multiple state updates, and that you can't catch all of them.

If you are using your Feature as part of an MVI approach, then the last (current) state of it should always be enough to render the View, so in these cases it shouldn't be an issue at all.

But, as you can use a Feature as a more generic tool, in some rare cases it can be valid that you absolutely need to catch and evaluate all state/news emissions.

Catching all emissions

In a cross-cutting concern case (e.g. logging) you probably want to go with middlewares.

If that's not the case, you might want to delay bootstrapping for whatever reason outside of Feature.

Don't

What you don't want to do is create "Init" Wish for example. That results in it being a repeatable and public action on the Feature (nothing is stopping you from pushing the same Wish even multiple times from any other part of the code), and is considered a smell.

Do

Rather, you can achieve the delay by injecting an Observable through the constructor of the Feature to the constructor of your Bootstrapper and use that stream to delay subscription:

// Feature
class SomeFeature(
    startSignal: Single<Unit>
    // ...
) : BaseFeature<Wish, Action, Effect, State, News>(
    // ...
    bootstrapper = BootStrapperImpl(startSignal)
    // ...
) {
    // ...

    class BootStrapperImpl(
        private val startSignal: Single<Unit>
    ) : Bootstrapper<Action> {
        override fun invoke(): Observable<Action> = 
            just(Action.BootstrappingAction)
                .delaySubscription(startSignal.toObservable())

    // ...
}


// Client code
val startSignal = PublishRelay.create<Unit>()
val feature = SomeFeature(startSignal)
// TODO do stuff, subscribe to feature, etc., and when you are ready:
startSignal.accept(Unit)

This way it's both non-repeatable and it's also not exposed to the public API (Wishes)