oldergod / android-architecture

MVI architecture Implementation of the ToDo app.
Apache License 2.0
669 stars 70 forks source link

Handling intents with no mapping actions #30

Closed jamolkhon closed 6 years ago

jamolkhon commented 6 years ago

When a dialog needs to be displayed in response to a button click, do you need an intent and a property (displayDialog: Boolean) in State data class? If yes, then how do you combine this intent->state flow with intent->action->result->state flow so that both use the latest state to produce a new one?

events
  .scan(State.default(), { state, event -> eventToState(event, state) })

events
  .map(::eventToAction)
  .compose(actionDispatcher)
  .scan(State.default(), { state, result -> resultToState(result, state) })

The two observables above produce states, but merging them is not an option because they both maintain their own states.

oldergod commented 6 years ago

When a dialog needs to be displayed in response to a button click, do you need an intent and a property (displayDialog: Boolean) in State data class?

Let's say the user rotates the screen after the dialog has been displayed; do you want the dialog to be opened then? If no, no need to go through the data flow. If yes, I would send an intent to update the state, yes.

This is something I never did so I don't have real answers.

Maybe a nice solution would separate UI only intents with business logic linked intents, like Intent -> Action -> Result -> BusinessLogicReducer -> UiLogicReducer and you could make it as ui only intents would be passed directly do the UiLogicReducer somehow.

Otherwise, to merge two streams as you're asking, you could have some UiOnlyEventIntent which would be

events
  .publish { shared ->
      Observable.merge(
        shared.ofType(UiOnlyEventIntent::class.java).compose(intentToResult),
        shared.filter({ it !is UiOnlyEvent }).compose(actionDispatcher)
      )
  }
  .scan(State.default(), reducer)
jamolkhon commented 6 years ago

do you want the dialog to be opened then?

Yes, definitely.

events
  .publish { shared ->
      Observable.merge(
        shared.ofType(UiOnlyEventIntent::class.java).compose(intentToResult),
        shared.filter({ it !is UiOnlyEvent }).compose(actionDispatcher)
      )
  }
  .scan(State.default(), reducer)

What would Result look like then? In my case Result class is in domain package/module. I do not create Result objects in UI or Presentation layer. I only map Results to State.

I was thinking about mapping Events (or Intents) to State directly but within a single stream. But I don't know how.

oldergod commented 6 years ago

So imagine you were to write all intents, actions and results. Keep those results, delete those actions then map those intents to their corresponding results.