Android における Model-View-Intent アーキテクチャ

Android における Model-View-Intent アーキテクチャ

Video: https://youtu.be/MIV7Mi9zko8

Android上の開発は非同期の扱いを避ける事はできないです。ネットワーク、フレームワーク、ユーザの操作などから非同期処理が発生してます。油断してしまうとアプリが複雑化してメンテナンスが難しくなります。皆様はネットワークからレスポンスを待っている途中、ユーザが画面をローテーションしても問題ないですか?並行してユーザがいろんな操作しても大丈夫でしょうか?

Model-View-Intent アーキテクチャは非同期処理が発生する前提で考えられたため、すべてがストリームとして扱ってデータの流れを一方通行にするかつ不変オブジェクトを使うのが MVI アーキテクチャの方針です。マルチスレッディングやAndroidのライフサイクルの対応から生じる問題がアーキテクチャによって解決されるおかげでアプリのロジックに集中できるようになり、コードが書きやすく、今後の保守も楽になります!

皆様が MVI アーキテクチャの強みを理解し自分で実現できるようにする目標で Kotlin と RxJava を使って並行処理、画面ローテーション、スナックバーを含んだ画面をどうやって作っていくかを語るつもりです!

05162bc961c3654218bf1839974a4f35?s=128

Benoît Quenaudon

February 08, 2018
Tweet

Transcript

  1. 2.
  2. 5.
  3. 11.
  4. 12.
  5. 15.
  6. 17.
  7. 18.

    sealed class TasksIntent { object InitialIntent : TasksIntent() object RefreshIntent

    : TasksIntent() data class ActivateTaskIntent(val task: Task) : TasksIntent() data class CompleteTaskIntent(val task: Task) : TasksIntent() }@
  8. 19.
  9. 20.

    sealed class TasksIntent { object InitialIntent : TasksIntent() object RefreshIntent

    : TasksIntent() data class ActivateTaskIntent(val task: Task) : TasksIntent() data class CompleteTaskIntent(val task: Task) : TasksIntent() object ClearCompletedTasksIntent : TasksIntent() }@
  10. 21.
  11. 22.

    sealed class TasksIntent { object InitialIntent : TasksIntent() object RefreshIntent

    : TasksIntent() data class ActivateTaskIntent(val task: Task) : TasksIntent() data class CompleteTaskIntent(val task: Task) : TasksIntent() object ClearCompletedTasksIntent : TasksIntent() data class ChangeFilterIntent(val filterType: TasksFilterType) : TasksIntent() }@
  12. 23.

    sealed class TasksIntent { object InitialIntent : TasksIntent() object RefreshIntent

    : TasksIntent() data class ActivateTaskIntent(val task: Task) : TasksIntent() data class CompleteTaskIntent(val task: Task) : TasksIntent() object ClearCompletedTasksIntent : TasksIntent() data class ChangeFilterIntent(val filterType: TasksFilterType) : TasksIntent() }@
  13. 25.

    class TasksFragment fun intents(): Observable<TasksIntent> { return initialIntent() }@a private

    fun initialIntent(): Observable<InitialIntent> { return Observable.just(InitialIntent) } }@
  14. 26.

    class TasksFragment fun_intents():_Observable<TasksIntent> { return Observable.merge(initialIntent(), refreshIntent()) }@a private fun

    refreshIntent(): Observable<RefreshIntent> { return RxSwipeRefreshLayout.refreshes(swipeRefreshLayout) .map { RefreshIntent } }@ }@
  15. 31.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> is RefreshIntent -> is ActivateTaskIntent -> is CompleteTaskIntent -> is ClearCompletedTasksIntent -> is ChangeFilterIntent -> }@
  16. 32.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> is RefreshIntent -> is ActivateTaskIntent -> is CompleteTaskIntent -> is ClearCompletedTasksIntent -> is ChangeFilterIntent -> }@
  17. 33.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> is RefreshIntent -> is ActivateTaskIntent -> is CompleteTaskIntent -> is ClearCompletedTasksIntent -> is ChangeFilterIntent -> }@
  18. 34.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> LoadAndFilterTasksAction(TasksFilterType.ALL_TASKS) is RefreshIntent -> is ActivateTaskIntent -> is CompleteTaskIntent -> is ClearCompletedTasksIntent -> is ChangeFilterIntent -> }@
  19. 35.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> LoadAndFilterTasksAction(TasksFilterType.ALL_TASKS) is RefreshIntent -> LoadTasksAction is ActivateTaskIntent -> is CompleteTaskIntent -> is ClearCompletedTasksIntent -> is ChangeFilterIntent -> }@
  20. 36.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> LoadAndFilterTasksAction(TasksFilterType.ALL_TASKS) is RefreshIntent -> LoadTasksAction is ActivateTaskIntent -> ActivateTaskAction(intent.task) is CompleteTaskIntent -> is ClearCompletedTasksIntent -> is ChangeFilterIntent -> }@
  21. 37.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> LoadAndFilterTasksAction(TasksFilterType.ALL_TASKS) is RefreshIntent -> LoadTasksAction is ActivateTaskIntent -> ActivateTaskAction(intent.task) is CompleteTaskIntent -> CompleteTaskAction(intent.task) is ClearCompletedTasksIntent -> is ChangeFilterIntent -> }@
  22. 38.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> LoadAndFilterTasksAction(TasksFilterType.ALL_TASKS) is RefreshIntent -> LoadTasksAction is ActivateTaskIntent -> ActivateTaskAction(intent.task) is CompleteTaskIntent -> CompleteTaskAction(intent.task) is ClearCompletedTasksIntent -> ClearCompletedTasksAction is ChangeFilterIntent -> }@
  23. 39.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> LoadAndFilterTasksAction(TasksFilterType.ALL_TASKS) is RefreshIntent -> LoadTasksAction is ActivateTaskIntent -> ActivateTaskAction(intent.task) is CompleteTaskIntent -> CompleteTaskAction(intent.task) is ClearCompletedTasksIntent -> ClearCompletedTasksAction is ChangeFilterIntent -> LoadAndFilterTasksAction(intent.filterType) }@
  24. 40.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> LoadAndFilterTasksAction(TasksFilterType.ALL_TASKS) is RefreshIntent -> LoadTasksAction is ActivateTaskIntent -> ActivateTaskAction(intent.task) is CompleteTaskIntent -> CompleteTaskAction(intent.task) is ClearCompletedTasksIntent -> ClearCompletedTasksAction is ChangeFilterIntent -> LoadAndFilterTasksAction(intent.filterType) }@
  25. 41.

    private TasksAction actionFromIntent(MviIntent intent) { if (intent instanceof InitialIntent) {

    return LoadTasks.loadAndFilter(true, TasksFilterType.ALL_TASKS); } if (intent instanceof ChangeFilterIntent) { return LoadTasks.loadAndFilter(false, ((ChangeFilterIntent) intent).filterType()); } if (intent instanceof RefreshIntent) { return LoadTasks.load(((RefreshIntent) intent).forceUpdate()); } if (intent instanceof ActivateTaskIntent) { return ActivateTaskAction.create(((ActivateTaskIntent) intent).task()); } if (intent instanceof CompleteTaskIntent) { return CompleteTaskAction.create(((CompleteTaskIntent) intent).task()); } if (intent instanceof ClearCompletedTasksIntent) { return ClearCompletedTasksAction.create(); } throw new IllegalArgumentException("do not know how to treat this intent " + intent); }
  26. 42.

    private TasksAction actionFromIntent(MviIntent intent) { if (intent instanceof InitialIntent) {

    return LoadTasks.loadAndFilter(true, TasksFilterType.ALL_TASKS); } if (intent instanceof ChangeFilterIntent) { return LoadTasks.loadAndFilter(false, ((ChangeFilterIntent) intent).filterType()); } if (intent instanceof RefreshIntent) { return LoadTasks.load(((RefreshIntent) intent).forceUpdate()); } if (intent instanceof ActivateTaskIntent) { return ActivateTaskAction.create(((ActivateTaskIntent) intent).task()); } if (intent instanceof CompleteTaskIntent) { return CompleteTaskAction.create(((CompleteTaskIntent) intent).task()); } if (intent instanceof ClearCompletedTasksIntent) { return ClearCompletedTasksAction.create(); } throw new IllegalArgumentException("do not know how to treat this intent " + intent); }
  27. 43.

    private TasksAction actionFromIntent(MviIntent intent) { if (intent instanceof InitialIntent) {

    return LoadTasks.loadAndFilter(true, TasksFilterType.ALL_TASKS); } if (intent instanceof ChangeFilterIntent) { return LoadTasks.loadAndFilter(false, ((ChangeFilterIntent) intent).filterType()); } if (intent instanceof RefreshIntent) { return LoadTasks.load(((RefreshIntent) intent).forceUpdate()); } if (intent instanceof ActivateTaskIntent) { return ActivateTaskAction.create(((ActivateTaskIntent) intent).task()); } if (intent instanceof CompleteTaskIntent) { return CompleteTaskAction.create(((CompleteTaskIntent) intent).task()); } if (intent instanceof ClearCompletedTasksIntent) { return ClearCompletedTasksAction.create(); } throw new IllegalArgumentException("do not know how to treat this intent " + intent); }
  28. 44.

    private TasksAction actionFromIntent(MviIntent intent) { if (intent instanceof InitialIntent) {

    return LoadTasks.loadAndFilter(true, TasksFilterType.ALL_TASKS); } if (intent instanceof ChangeFilterIntent) { return LoadTasks.loadAndFilter(false, ((ChangeFilterIntent) intent).filterType()); } if (intent instanceof RefreshIntent) { return LoadTasks.load(((RefreshIntent) intent).forceUpdate()); } if (intent instanceof ActivateTaskIntent) { return ActivateTaskAction.create(((ActivateTaskIntent) intent).task()); } if (intent instanceof CompleteTaskIntent) { return CompleteTaskAction.create(((CompleteTaskIntent) intent).task()); } if (intent instanceof ClearCompletedTasksIntent) { return ClearCompletedTasksAction.create(); } throw new IllegalArgumentException("do not know how to treat this intent " + intent); }
  29. 45.

    fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent

    -> LoadAndFilterTasksAction(TasksFilterType.ALL_TASKS) is RefreshIntent -> LoadTasksAction is ActivateTaskIntent -> ActivateTaskAction(intent.task) is CompleteTaskIntent -> CompleteTaskAction(intent.task) is ClearCompletedTasksIntent -> ClearCompletedTasksAction is ChangeFilterIntent -> LoadAndFilterTasksAction(intent.filterType) }@
  30. 46.

    sealed class TasksAction { data class LoadAndFilterTasksAction(val filterType: TasksFilterType) :

    TasksAction() object LoadTasksAction : TasksAction() data class ActivateTaskAction(val task: Task) : TasksAction() data class CompleteTaskAction(val task: Task) : TasksAction() object ClearCompletedTasksAction : TasksAction() }@
  31. 48.

    Action Load and Filter Tasks Logic Clear Completed Task Logic

    Complete Task Logic Activate Task Logic Load Tasks Logic Result
  32. 53.

    var actionProcessor: ObservableTransformer<TasksAction, TasksResult> = ObservableTransformer { actions: Observable<TasksAction> ->

    actions.publish { shared -> Observable.merge( shared.ofType(LoadTasksAction::class.java).compose(loadTasksProcessor) )a }b }c
  33. 54.

    var actionProcessor: ObservableTransformer<TasksAction, TasksResult> = ObservableTransformer { actions: Observable<TasksAction> ->

    actions.publish { shared -> Observable.merge( shared.ofType(LoadTasksAction::class.java).compose(loadTasksProcessor), shared.ofType(LoadAndFilterTasksAction::class.java).compose(loadAndFilterTasksProcessor) )a }b }c
  34. 55.

    var actionProcessor: ObservableTransformer<TasksAction, TasksResult> = ObservableTransformer { actions: Observable<TasksAction> ->

    actions.publish { shared -> Observable.merge( shared.ofType(LoadTasksAction::class.java).compose(loadTasksProcessor), shared.ofType(LoadAndFilterTasksAction::class.java).compose(loadAndFilterTasksProcessor), shared.ofType(ActivateTaskAction::class.java).compose(activateTaskProcessor) )a }b }c
  35. 56.

    var actionProcessor: ObservableTransformer<TasksAction, TasksResult> = ObservableTransformer { actions: Observable<TasksAction> ->

    actions.publish { shared -> Observable.merge( shared.ofType(LoadTasksAction::class.java).compose(loadTasksProcessor), shared.ofType(LoadAndFilterTasksAction::class.java).compose(loadAndFilterTasksProcessor), shared.ofType(ActivateTaskAction::class.java).compose(activateTaskProcessor), shared.ofType(ClearCompletedTasksAction::class.java).compose(clearCompletedTasksProcessor) )a }b }c
  36. 57.

    var actionProcessor: ObservableTransformer<TasksAction, TasksResult> = ObservableTransformer { actions: Observable<TasksAction> ->

    actions.publish { shared -> Observable.merge( shared.ofType(LoadTasksAction::class.java).compose(loadTasksProcessor), shared.ofType(LoadAndFilterTasksAction::class.java).compose(loadAndFilterTasksProcessor), shared.ofType(ActivateTaskAction::class.java).compose(activateTaskProcessor), shared.ofType(ClearCompletedTasksAction::class.java).compose(clearCompletedTasksProcessor), shared.ofType(CompleteTaskAction::class.java).compose(completeTaskProcessor) )a }b }c
  37. 58.

    var actionProcessor: ObservableTransformer<TasksAction, TasksResult> = ObservableTransformer { actions: Observable<TasksAction> ->

    actions.publish { shared -> Observable.merge( shared.ofType(LoadTasksAction::class.java).compose(loadTasksProcessor), shared.ofType(LoadAndFilterTasksAction::class.java).compose(loadAndFilterTasksProcessor), shared.ofType(ActivateTaskAction::class.java).compose(activateTaskProcessor), shared.ofType(ClearCompletedTasksAction::class.java).compose(clearCompletedTasksProcessor), shared.ofType(CompleteTaskAction::class.java).compose(completeTaskProcessor) )a }b }c
  38. 60.

    val loadTasksProcessor = ObservableTransformer { actions: Observable<LoadTasksAction> -> actions.switchMap {

    tasksRepository.getTasks() // Observable<List<Tasks>> .startWith(LoadTasksResult.InFlight) }@ }@
  39. 61.

    val loadTasksProcessor = ObservableTransformer { actions: Observable<LoadTasksAction> -> actions.switchMap {

    tasksRepository.getTasks() // Observable<List<Tasks>> .startWith(LoadTasksResult.InFlight) .map { tasks -> LoadTasksResult.Success(tasks) } }@ }@
  40. 62.

    val loadTasksProcessor = ObservableTransformer { actions: Observable<LoadTasksAction> -> actions.switchMap {

    tasksRepository.getTasks() // Observable<List<Tasks>> .startWith(LoadTasksResult.InFlight) .map { tasks -> LoadTasksResult.Success(tasks) } .onErrorReturn { t -> loadTasksResult.Failure(t) } }@ }@
  41. 63.

    val loadTasksProcessor = ObservableTransformer { actions: Observable<LoadTasksAction> -> actions.switchMap {

    tasksRepository.getTasks() // Observable<List<Tasks>> .startWith(LoadTasksResult.InFlight) .map { tasks -> LoadTasksResult.Success(tasks) } .onErrorReturn { t -> loadTasksResult.Failure(t) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) }@ }@
  42. 64.

    val loadTasksProcessor = ObservableTransformer { actions: Observable<LoadTasksAction> -> actions.switchMap {

    tasksRepository.getTasks() // Observable<List<Tasks>> .startWith(LoadTasksResult.InFlight) .map { tasks -> LoadTasksResult.Success(tasks) } .onErrorReturn { t -> loadTasksResult.Failure(t) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) }@ }@
  43. 65.

    val loadTasksProcessor = ObservableTransformer { actions: Observable<LoadTasksAction> -> actions.switchMap {

    tasksRepository.getTasks() // Observable<List<Tasks>> .map { tasks -> LoadTasksResult.Success(tasks) } .onErrorReturn { t -> loadTasksResult.Failure(t) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .startWith(LoadTasksResult.InFlight) }@ }@
  44. 66.

    var actionProcessor: ObservableTransformer<TasksAction, TasksResult> = ObservableTransformer { actions: Observable<TasksAction> ->

    actions.publish { shared -> Observable.merge( shared.ofType(LoadTasksAction::class.java).compose(loadTasksProcessor), shared.ofType(LoadAndFilterTasksAction::class.java).compose(loadAndFilterTasksProcessor), shared.ofType(ActivateTaskAction::class.java).compose(activateTaskProcessor), shared.ofType(ClearCompletedTasksAction::class.java).compose(clearCompletedTasksProcessor), shared.ofType(CompleteTaskAction::class.java).compose(completeTaskProcessor) )a }b }c
  45. 67.

    intents // Observable<TasksIntent> .map { intent -> actionFromIntent(intent) } //

    Observable<TasksAction> .compose(actionProcessor) // Observable<TasksResult>
  46. 75.

    data class TasksViewState(z val isLoading: Boolean, val tasksFilterType: TasksFilterType, val

    tasks: List<Task>, val error: Throwable?, val taskComplete: Boolean, val taskActivated: Boolean, val completedTasksCleared: Boolean )@
  47. 76.

    data class TasksViewState(z val isLoading: Boolean, val tasksFilterType: TasksFilterType, val

    tasks: List<Task>, val error: Throwable?, val taskComplete: Boolean, val taskActivated: Boolean, val completedTasksCleared: Boolean )@
  48. 77.

    data class TasksViewState(z val isLoading: Boolean, val tasksFilterType: TasksFilterType, val

    tasks: List<Task>, val error: Throwable?, val taskComplete: Boolean, val taskActivated: Boolean, val completedTasksCleared: Boolean )@{ companion object Factory { fun default() = TasksViewState( isLoading = false, tasksFilterType = ALL_TASKS, tasks = emptyList(), error = null, taskComplete = false, taskActivated = false, completedTasksCleared = false) } }
  49. 81.

    intents // Observable<TasksIntent> .map { intent -> actionFromIntent(intent) } //

    Observable<TasksAction> .compose(actionProcessor) // Observable<TasksResult> .scan(TasksViewState.default(), reducer) // Observable<TasksViewState>
  50. 84.

    val reducer = BiFunction<TasksViewState, TasksResult, TasksViewState> { previousState: TasksViewState, result:

    TasksResult -> when (result)_{ is LoadTasksResult -> /***/ is LoadAndFilterTasksResult -> /***/ is CompleteTaskResult -> /***/ is ActivateTaskResult -> /***/ is ClearCompletedTasksResult -> /***/ }d }e
  51. 85.

    val reducer = BiFunction<TasksViewState, TasksResult, TasksViewState> { previousState: TasksViewState, result:

    TasksResult -> when (result)_{ is LoadTasksResult -> { when (result) { is LoadTasksResult.InFlight -> /***/ is LoadTasksResult.Failure -> /***/ is LoadTasksResult.Success -> /***/ }b }c is LoadAndFilterTasksResult -> /***/ is CompleteTaskResult -> /***/ is ActivateTaskResult -> /***/ is ClearCompletedTasksResult -> /***/ }d }e
  52. 86.

    val reducer = BiFunction<TasksViewState, TasksResult, TasksViewState> { previousState: TasksViewState, result:

    TasksResult -> when (result)_{ is LoadTasksResult -> { when (result) { is LoadTasksResult.InFlight -> previousState.copy(isLoading = true) is LoadTasksResult.Failure -> /***/ is LoadTasksResult.Success -> /***/ }b }c is LoadAndFilterTasksResult -> /***/ is CompleteTaskResult -> /***/ is ActivateTaskResult -> /***/ is ClearCompletedTasksResult -> /***/ }d }e
  53. 87.

    val reducer = BiFunction<TasksViewState, TasksResult, TasksViewState> { previousState: TasksViewState, result:

    TasksResult -> when (result)_{ is LoadTasksResult -> { when (result) { is LoadTasksResult.InFlight -> previousState.copy(isLoading = true) is LoadTasksResult.Failure -> previousState.copy(isLoading = false, error = result.error) is LoadTasksResult.Success -> /***/ }b }c is LoadAndFilterTasksResult -> /***/ is CompleteTaskResult -> /***/ is ActivateTaskResult -> /***/ is ClearCompletedTasksResult -> /***/ }d }e
  54. 88.

    val reducer = BiFunction<TasksViewState, TasksResult, TasksViewState> { previousState: TasksViewState, result:

    TasksResult -> when (result)_{ is LoadTasksResult -> { when (result) { is LoadTasksResult.InFlight -> previousState.copy(isLoading = true) is LoadTasksResult.Failure -> previousState.copy(isLoading = false, error = result.error) is LoadTasksResult.Success -> { previousState.copy(isLoading = false, tasks = result.tasks) }a }b }c is LoadAndFilterTasksResult -> /***/ is CompleteTaskResult -> /***/ is ActivateTaskResult -> /***/ is ClearCompletedTasksResult -> /***/ }d }e
  55. 89.

    val reducer = BiFunction<TasksViewState, TasksResult, TasksViewState> { previousState: TasksViewState, result:

    TasksResult -> when (result)_{ is LoadTasksResult -> { when (result) { is LoadTasksResult.InFlight -> previousState.copy(isLoading = true) is LoadTasksResult.Failure -> previousState.copy(isLoading = false, error = result.error) is LoadTasksResult.Success -> { previousState.copy(isLoading = false, tasks = result.tasks) }a }b }c is LoadAndFilterTasksResult -> /***/ is CompleteTaskResult -> /***/ is ActivateTaskResult -> /***/ is ClearCompletedTasksResult -> /***/ }d }e
  56. 91.

    intents // Observable<TasksIntent> .map { intent -> actionFromIntent(intent) } //

    Observable<TasksAction> .compose(actionProcessor) // Observable<TasksResult> .scan(TasksViewState.default(), reducer) // Observable<TasksViewState>
  57. 92.

    intents // Observable<TasksIntent> .map { intent -> actionFromIntent(intent) } //

    Observable<TasksAction> .compose(actionProcessor) // Observable<TasksResult> .scan(TasksViewState.default(), reducer) // Observable<TasksViewState> .subscribe { state -> render(state) }
  58. 96.

    fun render(state: TasksViewState) { swipeRefreshLayout.isRefreshing = state.isLoading if (state.error !=

    null) { showLoadingTasksError() return }a if (state.taskActivated) { showMessage(getString(R.string.task_marked_active)) }b if (state.taskComplete) { showMessage(getString(R.string.task_marked_complete)) }c if (state.completedTasksCleared) { showMessage(getString(R.string.completed_tasks_cleared)) }d }u
  59. 97.

    if (state.taskActivated) { showMessage(getString(R.string.task_marked_active)) }b if (state.taskComplete) { showMessage(getString(R.string.task_marked_complete)) }c

    if (state.completedTasksCleared) { showMessage(getString(R.string.completed_tasks_cleared)) }d if (state.tasks.isEmpty()) { when (state.tasksFilterType) {z ACTIVE_TASKS -> showNoActiveTasks() COMPLETED_TASKS -> showNoCompletedTasks() ALL_TASKS -> showNoTasks() }e }_ }u
  60. 98.

    showMessage(getString(R.string.completed_tasks_cleared)) }d if (state.tasks.isEmpty()) { when (state.tasksFilterType) {z ACTIVE_TASKS ->

    showNoActiveTasks() COMPLETED_TASKS -> showNoCompletedTasks() ALL_TASKS -> showNoTasks() }e }_else { listAdapter.replaceData(state.tasks) tasksView.visibility = View.VISIBLE noTasksView.visibility = View.GONE when (state.tasksFilterType) { ACTIVE_TASKS -> showActiveFilterLabel() COMPLETED_TASKS -> showCompletedFilterLabel() ALL_TASKS -> showAllFilterLabel() }g }h }u
  61. 99.

    fun render(state: TasksViewState) { swipeRefreshLayout.isRefreshing = state.isLoading if (state.error !=

    null) { showLoadingTasksError() return }a if (state.taskActivated) { showMessage(getString(R.string.task_marked_active)) }b if (state.taskComplete) { showMessage(getString(R.string.task_marked_complete)) }c if (state.completedTasksCleared) { showMessage(getString(R.string.completed_tasks_cleared)) }d if (state.tasks.isEmpty()) { when (state.tasksFilterType) { ACTIVE_TASKS -> showNoActiveTasks() COMPLETED_TASKS -> showNoCompletedTasks() ALL_TASKS -> showNoTasks() }e }_else { listAdapter.replaceData(state.tasks) tasksView.visibility = View.VISIBLE noTasksView.visibility = View.GONE when (state.tasksFilterType) { ACTIVE_TASKS -> showActiveFilterLabel() COMPLETED_TASKS -> showCompletedFilterLabel() ALL_TASKS -> showAllFilterLabel() }g }h }u
  62. 106.
  63. 108.
  64. 109.

    USER USER INTERFACE VIEW MODEL INTENTS RENDER Intent Result Action

    State INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY
  65. 110.

    interface TasksUi { fun render(state: TasksViewState) fun intents(): Observable<TasksIntent> }@

    interface TasksViewModel { fun processIntents(intents: Observable<TasksIntent>) fun states(): Observable<TasksViewState> }@
  66. 111.

    interface TasksUi { fun render(state: TasksViewState) fun intents(): Observable<TasksIntent> }@

    interface TasksViewModel { fun processIntents(intents: Observable<TasksIntent>) fun states(): Observable<TasksViewState> }@
  67. 112.

    interface TasksUi { fun render(state: TasksViewState) fun intents(): Observable<TasksIntent> }@

    interface TasksViewModel { fun processIntents(intents: Observable<TasksIntent>) fun states(): Observable<TasksViewState> }@
  68. 118.

    USER USER INTERFACE VIEW MODEL INTENTS RENDER Intent Result Action

    State INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY C B A
  69. 119.

    INTENTS RENDER VIEW MODEL Intent Result Action State INTENT INTERPRETOR

    REDUCER PROCESSOR REPOSITORY B C A USER USER INTERFACE B C A
  70. 120.

    USER USER INTERFACE VIEW MODEL INTENTS RENDER Intent Result Action

    State INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY A
  71. 122.
  72. 123.

    USER INTERFACE VIEW MODEL INTENTS RENDER Intent Result Action State

    INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY
  73. 135.

    class TasksViewModel : ViewModel() { private val intentsSubject = PublishSubject.create<TasksIntent>()

    fun processIntents(intents: Observable<TasksIntent>) { intents.subscribe(intentsSubject) }b fun states(): Observable<TasksViewState> { }c }e
  74. 136.

    class TasksViewModel : ViewModel() { private val intentsSubject = PublishSubject.create<TasksIntent()

    val states: Observable<TasksViewState> = compose() fun processIntents(intents: Observable<TasksIntent>) { intents.subscribe(intentsSubject) }b private fun compose(): Observable<TasksViewState> { }c }e
  75. 137.

    class TasksViewModel : ViewModel() { private val intentsSubject = PublishSubject.create<TasksIntent>()

    val states: Observable<TasksViewState> = compose() fun processIntents(intents: Observable<TasksIntent>) { intents.subscribe(intentsSubject) }b private fun compose(): Observable<TasksViewState> { return intentsSubject .map { intent -> actionFromIntent(intent) } .compose(actionProcessor) .scan(TasksViewState.default(), reducer) }c }e
  76. 138.

    class TasksViewModel : ViewModel() { private val intentsSubject = PublishSubject.create<TasksIntent>()

    val states: Observable<TasksViewState> = compose() fun processIntents(intents: Observable<TasksIntent>) { intents.subscribe(intentsSubject) }b private fun compose(): Observable<TasksViewState> { return intentsSubject .map { intent -> actionFromIntent(intent) } .compose(actionProcessor) .scan(TasksViewState.default(), reducer) .replay(1) .autoConnect(0) }c }e
  77. 139.

    class TasksViewModel : ViewModel() { private val intentsSubject = PublishSubject.create<TasksIntent>()

    val states: Observable<TasksViewState> = compose() fun processIntents(intents: Observable<TasksIntent>) { intents.subscribe(intentsSubject) }b private fun compose(): Observable<TasksViewState> { return intentsSubject .map { intent -> actionFromIntent(intent) } .compose(actionProcessor) .scan(TasksViewState.default(), reducer) .replay(1) .autoConnect(0) }c }e
  78. 140.
  79. 141.

    class TasksFragment : Fragment() { private val viewModel: TasksViewModel by

    lazy(NONE) { ViewModelProviders.of(this).get(TasksViewModel::class.java) }a fun intents(): Observable<TasksIntent> { /***/ } fun render(state: TasksViewState) { /***/ } }d
  80. 142.

    class TasksFragment : Fragment() { private val viewModel: TasksViewModel by

    lazy(NONE) { ViewModelProviders.of(this).get(TasksViewModel::class.java) }a fun intents(): Observable<TasksIntent> { /***/ } fun render(state: TasksViewState) { /***/ } override fun onStart() { viewModel.states().subscribe(this::render) }b }d
  81. 143.

    class TasksFragment : Fragment() { private val viewModel: TasksViewModel by

    lazy(NONE) { ViewModelProviders.of(this).get(TasksViewModel::class.java) }a private val disposables = CompositeDisposable()a fun intents(): Observable<TasksIntent> { /***/ } fun render(state: TasksViewState) { /***/ } override fun onStart() { disposables.add(viewModel.states().subscribe(this::render)) }b }d
  82. 144.

    class TasksFragment : Fragment() { private val viewModel: TasksViewModel by

    lazy(NONE) { ViewModelProviders.of(this).get(TasksViewModel::class.java) }a private val disposables = CompositeDisposable() fun intents(): Observable<TasksIntent> { /***/ } fun render(state: TasksViewState) { /***/ } override fun onStart() { disposables.add(viewModel.states().subscribe(this::render)) viewModel.processIntents(intents()) }b }d
  83. 145.

    class TasksFragment : Fragment() { private val viewModel: TasksViewModel by

    lazy(NONE) { ViewModelProviders.of(this).get(TasksViewModel::class.java) }a private val disposables = CompositeDisposable() fun intents(): Observable<TasksIntent> { /***/ } fun render(state: TasksViewState) { /***/ } override fun onStart() { disposables.add(viewModel.states().subscribe(this::render)) viewModel.processIntents(intents()) }b override fun onDestroy() { disposables.dispose() super.onDestroy() }c }d
  84. 146.

    class TasksFragment : Fragment() { private val viewModel: TasksViewModel by

    lazy(NONE) { ViewModelProviders.of(this).get(TasksViewModel::class.java) }a private val disposables = CompositeDisposable() fun intents(): Observable<TasksIntent> { /***/ } fun render(state: TasksViewState) { /***/ } override fun onStart() { disposables.add(viewModel.states().subscribe(this::render)) viewModel.processIntents(intents()) }b override fun onDestroy() { disposables.dispose() super.onDestroy() }c }d
  85. 147.

    class TasksFragment : Fragment() { private val viewModel: TasksViewModel by

    lazy(NONE) { ViewModelProviders.of(this).get(TasksViewModel::class.java) }a private val disposables = CompositeDisposable() fun intents(): Observable<TasksIntent> { /***/ } fun render(state: TasksViewState) { /***/ } override fun onStart() { disposables.add(viewModel.states().subscribe(this::render)) viewModel.processIntents(intents()) }b override fun onDestroy() { disposables.dispose() super.onDestroy() }c }d
  86. 149.

    class TasksFragment fun intents(): Observable<TasksIntent> { return Observable.merge(initialIntent(), refreshIntent(), completeTaskIntent(),

    activateTaskIntent(), clearCompletedTaskIntent(), changeFilterIntent()) }@ }@ fun actionFromIntent(intent: TasksIntent): TasksAction = when (intent) { is InitialIntent -> LoadAndFilterTasksAction /***/ }@
  87. 150.

    private fun compose(): Observable<TasksViewState> { return intentsSubject .map { this.actionFromIntent(it)

    } .compose(actionProcessorHolder.actionProcessor) .scan(TasksViewState.idle(), reducer) .replay(1) .autoConnect(0) }a
  88. 151.

    private fun compose(): Observable<TasksViewState> { return intentsSubject .compose(intentFilter) .map {

    this.actionFromIntent(it) } .compose(actionProcessorHolder.actionProcessor) .scan(TasksViewState.idle(), reducer) .replay(1) .autoConnect(0) }a
  89. 152.

    private fun compose(): Observable<TasksViewState> { return intentsSubject .compose(intentFilter) .map {

    this.actionFromIntent(it) } .compose(actionProcessorHolder.actionProcessor) .scan(TasksViewState.idle(), reducer) .replay(1) .autoConnect(0) }a private val intentFilter: ObservableTransformer<TasksIntent, TasksIntent> get() = ObservableTransformer { intents -> }z
  90. 153.

    private fun compose(): Observable<TasksViewState> { return intentsSubject .compose(intentFilter) .map {

    this.actionFromIntent(it) } .compose(actionProcessorHolder.actionProcessor) .scan(TasksViewState.idle(), reducer) .replay(1) .autoConnect(0) }a private val intentFilter: ObservableTransformer<TasksIntent, TasksIntent> get() = ObservableTransformer { intents -> intents.publish { shared -> Observable.merge()w }o }z
  91. 154.

    private fun compose(): Observable<TasksViewState> { return intentsSubject .compose(intentFilter) .map {

    this.actionFromIntent(it) } .compose(actionProcessorHolder.actionProcessor) .scan(TasksViewState.idle(), reducer) .replay(1) .autoConnect(0) }a private val intentFilter: ObservableTransformer<TasksIntent, TasksIntent> get() = ObservableTransformer { intents -> intents.publish { shared -> Observable.merge( shared.ofType(InitialIntent::class.java).take(1) )w }o }z
  92. 155.

    private fun compose(): Observable<TasksViewState> { return intentsSubject .compose(intentFilter) .map {

    this.actionFromIntent(it) } .compose(actionProcessorHolder.actionProcessor) .scan(TasksViewState.idle(), reducer) .replay(1) .autoConnect(0) }a private val intentFilter: ObservableTransformer<TasksIntent, TasksIntent> get() = ObservableTransformer { intents -> intents.publish { shared -> Observable.merge( shared.ofType(InitialIntent::class.java).take(1), shared.notOfType(InitialIntent::class.java) )w }o }z
  93. 156.

    private fun compose(): Observable<TasksViewState> { return intentsSubject .compose(intentFilter) .map {

    this.actionFromIntent(it) } .compose(actionProcessorHolder.actionProcessor) .scan(TasksViewState.idle(), reducer) .replay(1) .autoConnect(0) }a private val intentFilter: ObservableTransformer<TasksIntent, TasksIntent> get() = ObservableTransformer { intents -> intents.publish { shared -> Observable.merge( shared.ofType(InitialIntent::class.java).take(1), shared.notOfType(InitialIntent::class.java) )w }o }z
  94. 158.

    USER INTERFACE VIEW MODEL INTENTS RENDER Intent Result Action State

    INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY
  95. 161.

    USER INTERFACE VIEW MODEL INTENTS RENDER Intent Result Action State

    INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY
  96. 162.

    USER INTERFACE VIEW MODEL SUBJECT TEST OBSERVER Intent Result Action

    State INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY
  97. 163.

    USER INTERFACE VIEW MODEL INTENTS RENDER Intent Result Action State

    INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY
  98. 164.

    USER INTERFACE VIEW MODEL INTENTS RENDER Intent Result Action State

    INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY
  99. 165.

    USER INTERFACE VIEW MODEL INTENTS RENDER Intent Result Action State

    INTENT INTERPRETOR REDUCER PROCESSOR REPOSITORY
  100. 166.

    Action Load and Filter Tasks Logic Clear Completed Task Logic

    Complete Task Logic Activate Task Logic Load Tasks Logic Result PROCESSOR
  101. 167.

    Load and Filter Tasks Logic Clear Completed Task Logic Complete

    Task Logic Activate Task Logic Load Tasks Logic SUBJECT TEST OBSERVER SUBJECT SUBJECT SUBJECT SUBJECT TEST OBSERVER TEST OBSERVER TEST OBSERVER TEST OBSERVER
  102. 168.