Slide 1

Slide 1 text

Handling View State and Events with RainbowCake Márton Braun zsmb.co zsmb13

Slide 2

Slide 2 text

RainbowCake rainbowcake/rainbowcake

Slide 3

Slide 3 text

RainbowCake rainbowcake/rainbowcake An Android architecture framework library test

Slide 4

Slide 4 text

RainbowCake rainbowcake/rainbowcake An Android architecture library concept framework

Slide 5

Slide 5 text

RainbowCake rainbowcake/rainbowcake An Android architecture concept thingie library

Slide 6

Slide 6 text

RainbowCake rainbowcake/rainbowcake An Android architecture thingie framework concept

Slide 7

Slide 7 text

RainbowCake rainbowcake/rainbowcake An Android architecture framework test thingie

Slide 8

Slide 8 text

RainbowCake rainbowcake/rainbowcake An Android architecture framework libraries

Slide 9

Slide 9 text

RainbowCake rainbowcake/rainbowcake An Android architecture framework libraries guidance

Slide 10

Slide 10 text

RainbowCake rainbowcake/rainbowcake An Android architecture framework libraries guidance

Slide 11

Slide 11 text

RainbowCake rainbowcake/rainbowcake Threading Dependency injection Layers View state handling Navigation Models

Slide 12

Slide 12 text

MvRx airbnb/MvRx Orbit MVI babylonhealth/orbit-mvi Roxie ww-tech/roxie Mosby sockeqwe/mosby Eiffel etiennelenhart/Eiffel Uniflow uniflow-kt/uniflow-kt MVICore badoo/MVICore RainbowCake rainbowcake/rainbowcake Mobius spotify/mobius

Slide 13

Slide 13 text

2020 2019 Start of development Moved into a library Workshop Modules Koin support Testing module Repackage, website launch GitHub, MavenCentral AndroidX 1.0 MVICore MvRx Orbit MVI Uniflow Roxie [0]

Slide 14

Slide 14 text

ItemApi ItemDao NetworkDataSource DiskDataSource ShopInteractor UserInteractor ProfilePresenter CartPresenter HomePresenter ProfileViewModel CartViewModel HomeViewModel CartViewState ProfileViewState HomeViewState CartFragment ProfileFragment HomeFragment

Slide 15

Slide 15 text

Dao Data source Interactor Presenter ViewModel ViewState Fragment

Slide 16

Slide 16 text

ViewState ViewModel Presenter Interactor Data source Dao Fragment

Slide 17

Slide 17 text

ViewModel Presenter Interactor Data source Fragment

Slide 18

Slide 18 text

RainbowCake

Slide 19

Slide 19 text

Data source Interactor Presenter ViewModel Fragment

Slide 20

Slide 20 text

MVVM View access through an interface Separation of View and business logic Component between View and Model MVP

Slide 21

Slide 21 text

MVVM View access through an interface Separation of View and business logic Component between View and Model MVP Separation of View and business logic

Slide 22

Slide 22 text

MVVM View access through an interface Separation of View and business logic Component between View and Model MVP Separation of View and business logic Component between View and Model

Slide 23

Slide 23 text

MVVM View access through an interface Separation of View and business logic Component between View and Model MVP View access through an interface Separation of View and business logic Component between View and Model

Slide 24

Slide 24 text

Observable state exposed to the View MVVM View access through an interface Separation of View and business logic Component between View and Model MVP View access through an interface Separation of View and business logic Component between View and Model

Slide 25

Slide 25 text

View access through an interface MVVM Separation of View and business logic Component between View and Model Observable state exposed to the View MVP View access through an interface Separation of View and business logic Component between View and Model

Slide 26

Slide 26 text

MVVM Separation of View and business logic Component between View and Model Observable state exposed to the View Data binding* MVP View access through an interface Separation of View and business logic Component between View and Model

Slide 27

Slide 27 text

MVVM Separation of View and business logic Component between View and Model Observable state exposed to the View Data binding* MVI

Slide 28

Slide 28 text

MVVM Separation of View and business logic Component between View and Model Observable state exposed to the View Data binding* MVI Single View state object

Slide 29

Slide 29 text

MVVM Separation of View and business logic Component between View and Model Observable state exposed to the View Data binding* MVI Single View state object Reactive data streams

Slide 30

Slide 30 text

MVVM Separation of View and business logic Component between View and Model Observable state exposed to the View Data binding* MVI Single View state object Reactive data streams Input events as objects

Slide 31

Slide 31 text

MVVM Separation of View and business logic Component between View and Model Observable state exposed to the View Data binding* MVI Single View state object Reactive data streams Input events as objects Reducer

Slide 32

Slide 32 text

MVVM Separation of View and business logic Component between View and Model Observable state exposed to the View Data binding* MVI Single View state object Reactive data streams Input events as objects Reducer [1]

Slide 33

Slide 33 text

State handling

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

app RainbowCake

Slide 36

Slide 36 text

app RainbowCake abstract class RainbowCakeViewModel ViewModel() class ProfileViewModel : RainbowCakeViewModel() :

Slide 37

Slide 37 text

app RainbowCake abstract class RainbowCakeViewModel ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state } class ProfileViewModel : RainbowCakeViewModel() :

Slide 38

Slide 38 text

app RainbowCake abstract class RainbowCakeViewModel ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state } class ProfileViewModel : RainbowCakeViewModel() :

Slide 39

Slide 39 text

app RainbowCake abstract class RainbowCakeViewModel ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state } class ProfileViewModel : RainbowCakeViewModel() :

Slide 40

Slide 40 text

app RainbowCake abstract class RainbowCakeViewModel ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state } class ProfileViewModel : RainbowCakeViewModel() :

Slide 41

Slide 41 text

app RainbowCake abstract class RainbowCakeViewModel ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state } class ProfileViewModel : RainbowCakeViewModel() :

Slide 42

Slide 42 text

app RainbowCake abstract class RainbowCakeViewModel ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state } class ProfileViewModel : RainbowCakeViewModel() :

Slide 43

Slide 43 text

app RainbowCake abstract class RainbowCakeViewModel : ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state } class ProfileViewModel : RainbowCakeViewModel()

Slide 44

Slide 44 text

app RainbowCake abstract class RainbowCakeViewModel : ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state } class ProfileViewModel : RainbowCakeViewModel() sealed class ProfileViewState object Loading : ProfileViewState() data class ProfileLoaded(val name: String) : ProfileViewState() [2]

Slide 45

Slide 45 text

app RainbowCake abstract class RainbowCakeViewModel : ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state } class ProfileViewModel : RainbowCakeViewModel()

Slide 46

Slide 46 text

app RainbowCake abstract class RainbowCakeViewModel protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state.distinct() } class ProfileViewModel : RainbowCakeViewModel() : ViewModel() { [3]

Slide 47

Slide 47 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state.distinct() } class ProfileViewModel : RainbowCakeViewModel() : ViewModel() {

Slide 48

Slide 48 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state.distinct() init { _state.value = initialState } } class ProfileViewModel : RainbowCakeViewModel()

Slide 49

Slide 49 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state.distinct() init { _state.value = initialState } } class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { _state.value = Loading

Slide 50

Slide 50 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state.distinct() init { _state.value = initialState } } class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { _state.value = Loading _state.value = ProfileLoaded(loadUserName()) } } }

Slide 51

Slide 51 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state.distinct() init { _state.value = initialState } } class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { _state.value = _state.value = } } } _state.value!!.copy(isLoading = true) _state.value!!.copy(name = ) loadUserName()

Slide 52

Slide 52 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _state: MutableLiveData = MutableLiveData() val state: LiveData = _state.distinct() init { _state.value = initialState } }

Slide 53

Slide 53 text

RainbowCake protected var viewState: VS get() = _state.value!! set(value) { _state.postValue(value) } abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { } val state: LiveData = _state.distinct() init { _state.value = initialState } val _state: MutableLiveData = MutableLiveData() private

Slide 54

Slide 54 text

app RainbowCake class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { _state.value = _state.value!!.copy(isLoading = true) _state.value = _state.value!!.copy(name = loadUserName()) } } } abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected var viewState: VS get() = _state.value!! set(value) { _state.postValue(value) } }

Slide 55

Slide 55 text

app RainbowCake = = viewState viewState viewState viewState .copy(isLoading = true) .copy(name = loadUserName()) class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { } } } abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected var viewState: VS get() = _state.value!! set(value) { _state.postValue(value) } }

Slide 56

Slide 56 text

app RainbowCake loadUserName() ( ) Loading ProfileLoaded abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected var viewState: VS get() = _state.value!! set(value) { _state.postValue(value) } } class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { viewState = viewState = } } }

Slide 57

Slide 57 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected var viewState: VS get() = _state.value!! set(value) { _state.postValue(value) } } class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { viewState = Loading viewState = ProfileLoaded(loadUserName()) } } }

Slide 58

Slide 58 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected var viewState: VS get() = _state.value!! set(value) { _state.postValue(value) } } class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { viewState = Loading viewState = ProfileLoaded(loadUserName()) } } }

Slide 59

Slide 59 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected var viewState: VS get() = _state.value!! set(value) { _state.setValue(value) } } class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { viewState = Loading viewState = ProfileLoaded(loadUserName()) } } }

Slide 60

Slide 60 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected var viewState: VS get() = _state.value!! set(value) { _state.postValue(value) } } class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { viewState = Loading viewState = ProfileLoaded(loadUserName()) } } }

Slide 61

Slide 61 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected var viewState: VS get() = _state.value!! set(value) { _state.postValue(value) } } println(viewState is Loading) class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() { viewModelScope.launch { viewState = Loading viewState = ProfileLoaded(loadUserName()) } } } [4]

Slide 62

Slide 62 text

app RainbowCake ) : ViewModel() { protected var viewState: VS get() = _state.value!! set(value) { _state.postValue(value) } } class ProfileViewModel : RainbowCakeViewModel(Loading) { fun loadProfile() {

Slide 63

Slide 63 text

app RainbowCake abstract class RainbowCakeFragment : Fragment() class ProfileFragment : RainbowCakeFragment()

Slide 64

Slide 64 text

RainbowCake { protected lateinit var viewModel: ? } app class ProfileFragment : RainbowCakeFragment() abstract class RainbowCakeFragment : Fragment()

Slide 65

Slide 65 text

app RainbowCake abstract class RainbowCakeFragment Fragment() { protected lateinit var viewModel VM } class ProfileFragment : RainbowCakeFragment() : :

Slide 66

Slide 66 text

app RainbowCake abstract class RainbowCakeFragment : Fragment() { protected lateinit var viewModel: VM } class ProfileFragment : RainbowCakeFragment()

Slide 67

Slide 67 text

app RainbowCake abstract class RainbowCakeFragment> : Fragment() { protected lateinit var viewModel: VM } class ProfileFragment : RainbowCakeFragment() :

Slide 68

Slide 68 text

RainbowCake ? app abstract class RainbowCakeFragment> protected lateinit var viewModel: VM } class ProfileFragment : RainbowCakeFragment() : : Fragment(

Slide 69

Slide 69 text

app RainbowCake abstract class RainbowCakeFragment> protected lateinit var viewModel: VM } class ProfileFragment : RainbowCakeFragment() : Fragment() {

Slide 70

Slide 70 text

app RainbowCake abstract class RainbowCakeFragment> : Fragment() { protected lateinit var viewModel: VM } class ProfileFragment : RainbowCakeFragment()

Slide 71

Slide 71 text

RainbowCake abstract class RainbowCakeFragment> : Fragment() { protected lateinit var viewModel abstract fun provideViewModel(): VM @CallSuper override fun onAttach(context: Context) { super.onAttach(context) viewModel = provideViewModel() } app class ProfileFragment : RainbowCakeFragment() : : VM }

Slide 72

Slide 72 text

RainbowCake app class ProfileFragment : RainbowCakeFragment() { } abstract fun provideViewModel(): VM @CallSuper override fun onAttach(context: Context) { super.onAttach(context) viewModel = provideViewModel() } override fun provideViewModel() = getViewModelFromFactory() abstract class RainbowCakeFragment> : Fragment() { protected lateinit var viewModel: VM } [5]

Slide 73

Slide 73 text

RainbowCake app class ProfileFragment : RainbowCakeFragment() { } abstract fun provideViewModel(): VM @CallSuper override fun onAttach(context: Context) { super.onAttach(context) viewModel = provideViewModel() } override fun provideViewModel() = getViewModelFromFactory() abstract class RainbowCakeFragment> : Fragment() { protected lateinit var viewModel: VM }

Slide 74

Slide 74 text

RainbowCake @CallSuper override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.state.observe(viewLifecycleOwner, Observer { viewState -> }) } app class ProfileFragment : RainbowCakeFragment() { } abstract class RainbowCakeFragment> : Fragment() { protected lateinit var viewModel: VM }

Slide 75

Slide 75 text

RainbowCake @CallSuper override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.state.observe(viewLifecycleOwner, Observer { viewState -> }) } app class ProfileFragment : RainbowCakeFragment() { } abstract class RainbowCakeFragment> : Fragment() { protected lateinit var viewModel: VM } [6]

Slide 76

Slide 76 text

app RainbowCake : protected lateinit var viewModel: VM abstract class RainbowCakeFragment> : Fragment() { abstract fun render(viewState VS) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.state.observe(viewLifecycleOwner, Observer { viewState -> viewState?.let { render(it) } }) } @CallSuper } class ProfileFragment : RainbowCakeFragment() { override fun render(viewState: ProfileViewState) { when (viewState) { Loading -> { ... }

Slide 77

Slide 77 text

app RainbowCake override fun render(viewState: ProfileViewState) { when (viewState) { Loading -> { ... } is ProfileLoaded -> { ... } } } : abstract fun render(viewState VS) } abstract class RainbowCakeFragment> : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.state.observe(viewLifecycleOwner, Observer { viewState -> viewState?.let { render(it) } }) } class ProfileFragment : RainbowCakeFragment() { }

Slide 78

Slide 78 text

app override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) RainbowCake abstract class RainbowCakeFragment> : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.state.observe(viewLifecycleOwner, Observer { viewState -> viewState?.let { render(it) } }) } abstract fun render(viewState: VS) } override fun render(viewState: ProfileViewState) { when (viewState) { Loading -> { ... } is ProfileLoaded -> { ... } } } class ProfileFragment : RainbowCakeFragment() { } .exhaustive [2]

Slide 79

Slide 79 text

app override fun render(viewState: ProfileViewState) { when (viewState) { Loading -> { ... } is ProfileLoaded -> { ... } } } class ProfileFragment : RainbowCakeFragment() { } .exhaustive override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.loadProfile() }

Slide 80

Slide 80 text

Event handling

Slide 81

Slide 81 text

RainbowCake app abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { } class ProfileViewModel : RainbowCakeViewModel(Loading)

Slide 82

Slide 82 text

RainbowCake app class ProfileViewModel : RainbowCakeViewModel(Loading) abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { } SingleShotLiveData() events: MutableLiveData = val

Slide 83

Slide 83 text

RainbowCake app class ProfileViewModel : RainbowCakeViewModel(Loading) abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { } SingleShotLiveData() events: MutableLiveData = val [7]

Slide 84

Slide 84 text

RainbowCake app class ProfileViewModel : RainbowCakeViewModel(Loading) abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected _ val events: LiveData = _events } SingleShotLiveData() val events: MutableLiveData =

Slide 85

Slide 85 text

RainbowCake app class ProfileViewModel : RainbowCakeViewModel(Loading) abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _events: MutableLiveData = SingleShotLiveData() val events: LiveData = _events }

Slide 86

Slide 86 text

RainbowCake app class ProfileViewModel : RainbowCakeViewModel(Loading) abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _events: MutableLiveData = SingleShotLiveData() val events: LiveData = _events } abstract class RainbowCakeFragment< VS : Any, VE : Any, VM : RainbowCakeViewModel > : Fragment

Slide 87

Slide 87 text

RainbowCake app class ProfileViewModel : RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _events: MutableLiveData = SingleShotLiveData() val events: LiveData = _events } abstract class RainbowCakeFragment< VS : Any, VE : Any, VM : RainbowCakeViewModel > : Fragment

Slide 88

Slide 88 text

RainbowCake app class ProfileViewModel : RainbowCakeViewModel(Loading) abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _events: MutableLiveData = SingleShotLiveData() val events: LiveData = _events }

Slide 89

Slide 89 text

RainbowCake app class ProfileViewModel : RainbowCakeViewModel(Loading) abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _events: MutableLiveData = SingleShotLiveData() val events: LiveData = _events } interface OneShotEvent

Slide 90

Slide 90 text

RainbowCake app class ProfileViewModel RainbowCakeViewModel(Loading) abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { protected val _events: MutableLiveData = SingleShotLiveData() val events: LiveData = _events } interface OneShotEvent : { object SaveFailedEvent : OneShotEvent

Slide 91

Slide 91 text

RainbowCake private app class ProfileViewModel RainbowCakeViewModel(Loading) { object SaveFailedEvent : OneShotEvent fun saveProfile() { _events.value = SaveFailedEvent } } interface OneShotEvent : } val events: LiveData = _events abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { SingleShotLiveData() val _events: MutableLiveData = protected

Slide 92

Slide 92 text

RainbowCake private app class ProfileViewModel RainbowCakeViewModel(Loading) { object SaveFailedEvent : OneShotEvent fun saveProfile() { _events.value = SaveFailedEvent } } interface OneShotEvent : } val events: LiveData = _events abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { SingleShotLiveData() val _events: MutableLiveData = protected

Slide 93

Slide 93 text

app RainbowCake protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } interface OneShotEvent } val events: LiveData = _events abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { SingleShotLiveData() val _events: MutableLiveData = private class ProfileViewModel RainbowCakeViewModel(Loading) { object SaveFailedEvent : OneShotEvent fun saveProfile() { _events.value = SaveFailedEvent } } :

Slide 94

Slide 94 text

app RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val _events: MutableLiveData = SingleShotLiveData() val events: LiveData = _events protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } } interface OneShotEvent class ProfileViewModel : RainbowCakeViewModel(Loading) { object SaveFailedEvent : OneShotEvent fun saveProfile() { postEvent(SaveFailedEvent) } }

Slide 95

Slide 95 text

app RainbowCake

Slide 96

Slide 96 text

app RainbowCake abstract fun provideViewModel(): VM @CallSuper override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.state.observe(viewLifecycleOwner, Observer { viewState -> viewState?.let { render(it) } }) } abstract fun render(viewState: VS) override fun provideViewModel() = getViewModelFromFactory() override fun render(viewState: ProfileViewState) { when (viewState) { Loading -> { ... } is ProfileLoaded -> { ... } }.exhaustive } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.loadProfile() } class ProfileFragment : RainbowCakeFragment() { } } @CallSuper override fun onAttach(context: Context) { super.onAttach(context) viewModel = provideViewModel() } protected lateinit var viewModel: VM abstract class RainbowCakeFragment> : Fragment() {

Slide 97

Slide 97 text

app RainbowCake class ProfileFragment : RainbowCakeFragment() { } } @CallSuper override fun onAttach(context: Context) { super.onAttach(context) viewModel = provideViewModel() } protected lateinit var viewModel: VM abstract class RainbowCakeFragment> : Fragment() {

Slide 98

Slide 98 text

app RainbowCake @CallSuper override fun onAttach(context: Context) { super.onAttach(context) viewModel = provideViewModel() viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} } abstract class RainbowCakeFragment> : Fragment() { protected lateinit var viewModel: VM class ProfileFragment : RainbowCakeFragment() { } override fun onEvent(event: OneShotEvent) { when (event) { is SaveFailedEvent -> showErrorMessage()

Slide 99

Slide 99 text

app RainbowCake @CallSuper override fun onAttach(context: Context) { super.onAttach(context) viewModel = provideViewModel() viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} } abstract class RainbowCakeFragment> : Fragment() { protected lateinit var viewModel: VM class ProfileFragment : RainbowCakeFragment() { } override fun onEvent(event: OneShotEvent) { when (event) { is SaveFailedEvent -> showErrorMessage()

Slide 100

Slide 100 text

app RainbowCake @CallSuper override fun onAttach(context: Context) { super.onAttach(context) viewModel = provideViewModel() viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} } abstract class RainbowCakeFragment> : Fragment() { class ProfileFragment : RainbowCakeFragment() { } override fun onEvent(event: OneShotEvent) { when (event) { is SaveFailedEvent -> showErrorMessage() } }

Slide 101

Slide 101 text

ViewModel Fragment

Slide 102

Slide 102 text

ViewModel Fragment 1

Slide 103

Slide 103 text

ViewModel Fragment

Slide 104

Slide 104 text

ViewModel Fragment

Slide 105

Slide 105 text

ViewModel Fragment 2

Slide 106

Slide 106 text

ViewModel Fragment 3

Slide 107

Slide 107 text

ViewModel Fragment 3

Slide 108

Slide 108 text

ViewModel Fragment

Slide 109

Slide 109 text

app RainbowCake class ProfileViewModel : RainbowCakeViewModel(Loading) { object SaveFailedEvent : OneShotEvent fun saveProfile() { postEvent(SaveFailedEvent) } } abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val _events: MutableLiveData = SingleShotLiveData() val events: LiveData = _events protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } }

Slide 110

Slide 110 text

app RainbowCake class ProfileViewModel : RainbowCakeViewModel(Loading) { object SaveFailedEvent : OneShotEvent fun saveProfile() { postEvent(SaveFailedEvent) } } abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val _events: MutableLiveData = QueuedSingleShotLiveData() val events: LiveData = _events protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } } [8]

Slide 111

Slide 111 text

ViewModel Fragment

Slide 112

Slide 112 text

ViewModel Fragment 1

Slide 113

Slide 113 text

ViewModel Fragment 1 2

Slide 114

Slide 114 text

ViewModel Fragment 1 2

Slide 115

Slide 115 text

ViewModel Fragment 2

Slide 116

Slide 116 text

ViewModel Fragment

Slide 117

Slide 117 text

ViewModel Fragment Fragment Fragment

Slide 118

Slide 118 text

ViewModel Fragment Fragment Fragment

Slide 119

Slide 119 text

Fragment Fragment Fragment ViewModel

Slide 120

Slide 120 text

Fragment Fragment Fragment ViewModel

Slide 121

Slide 121 text

Fragment Fragment Fragment ViewModel

Slide 122

Slide 122 text

Fragment Fragment Fragment ViewModel

Slide 123

Slide 123 text

RainbowCake interface LiveDataCollection { fun observe(owner: LifecycleOwner, observer: Observer) } interface MutableLiveDataCollection : LiveDataCollection { fun setValue(value: T?) fun postValue(value: T?) }

Slide 124

Slide 124 text

RainbowCake class MutableLiveDataCollectionImpl( private val factory: () -> MutableLiveData ) : MutableLiveDataCollection { } interface LiveDataCollection { fun observe(owner: LifecycleOwner, observer: Observer) } interface MutableLiveDataCollection : LiveDataCollection { fun setValue(value: T?) fun postValue(value: T?) }

Slide 125

Slide 125 text

RainbowCake class MutableLiveDataCollectionImpl( private val factory: () -> MutableLiveData ) : MutableLiveDataCollection { } interface LiveDataCollection { fun observe(owner: LifecycleOwner, observer: Observer) } interface MutableLiveDataCollection : LiveDataCollection { fun setValue(value: T?) fun postValue(value: T?) }

Slide 126

Slide 126 text

RainbowCake class MutableLiveDataCollectionImpl( private val factory: () -> MutableLiveData ) : MutableLiveDataCollection { private val activeLiveData: MutableSet> = mutableSetOf() interface LiveDataCollection { fun observe(owner: LifecycleOwner, observer: Observer) } interface MutableLiveDataCollection : LiveDataCollection { fun setValue(value: T?) fun postValue(value: T?) } }

Slide 127

Slide 127 text

RainbowCake /* ... */ interface LiveDataCollection { fun observe(owner: LifecycleOwner, observer: Observer) } interface MutableLiveDataCollection : LiveDataCollection { fun setValue(value: T?) fun postValue(value: T?) } } class MutableLiveDataCollectionImpl( private val factory: () -> MutableLiveData ) : MutableLiveDataCollection { private val activeLiveData: MutableSet> = mutableSetOf() override fun observe(owner: LifecycleOwner, observer: Observer) { } val liveData = factory() activeLiveData += liveData // ...

Slide 128

Slide 128 text

RainbowCake val liveData = factory() activeLiveData += liveData // ... interface LiveDataCollection { fun observe(owner: LifecycleOwner, observer: Observer) } interface MutableLiveDataCollection : LiveDataCollection { fun setValue(value: T?) fun postValue(value: T?) } } override fun setValue(value: T?) { activeLiveData.forEach { it.setValue(value) } } override fun postValue(value: T?) { activeLiveData.forEach { it.postValue(value) } } class MutableLiveDataCollectionImpl( private val factory: () -> MutableLiveData ) : MutableLiveDataCollection { private val activeLiveData: MutableSet> = mutableSetOf() override fun observe(owner: LifecycleOwner, observer: Observer) { } /* ... */

Slide 129

Slide 129 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val _events: MutableLiveData = QueuedSingleShotLiveData() val events: LiveData = _events protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } } abstract class RainbowCakeFragment> : Fragment() { override fun onAttach(context: Context) { // ... viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} }

Slide 130

Slide 130 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val _events: MutableLiveData = QueuedSingleShotLiveData() val events: LiveData = _events protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } } abstract class RainbowCakeFragment> : Fragment() { override fun onAttach(context: Context) { // ... viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} }

Slide 131

Slide 131 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val _events: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val events: LiveData = _events protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } } abstract class RainbowCakeFragment> : Fragment() { override fun onAttach(context: Context) { // ... viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} }

Slide 132

Slide 132 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val _events: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val events: LiveData = _events protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } } abstract class RainbowCakeFragment> : Fragment() { override fun onAttach(context: Context) { // ... viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} }

Slide 133

Slide 133 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val _events: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val events: LiveDataCollection = _events protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } } abstract class RainbowCakeFragment> : Fragment() { override fun onAttach(context: Context) { // ... viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} }

Slide 134

Slide 134 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val _events: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val events: LiveDataCollection = _events protected fun postEvent(event: OneShotEvent) { _events.postValue(event) } } abstract class RainbowCakeFragment> : Fragment() { override fun onAttach(context: Context) { // ... viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} }

Slide 135

Slide 135 text

ViewModel Fragment

Slide 136

Slide 136 text

ViewModel Fragment 1

Slide 137

Slide 137 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val viewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::ActiveOnlySingleShotLiveData) val events: LiveDataCollection = viewEvents protected fun postEvent(event: OneShotEvent) { viewEvents.postValue(event) } } private val queuedViewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val queuedEvents: LiveDataCollection = queuedViewEvents protected fun postQueuedEvent(event: QueuedOneShotEvent) { queuedViewEvents.postValue(event) }

Slide 138

Slide 138 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val viewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::ActiveOnlySingleShotLiveData) val events: LiveDataCollection = viewEvents protected fun postEvent(event: OneShotEvent) { viewEvents.postValue(event) } } private val queuedViewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val queuedEvents: LiveDataCollection = queuedViewEvents protected fun postQueuedEvent(event: QueuedOneShotEvent) { queuedViewEvents.postValue(event) }

Slide 139

Slide 139 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val viewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::ActiveOnlySingleShotLiveData) val events: LiveDataCollection = viewEvents protected fun postEvent(event: OneShotEvent) { viewEvents.postValue(event) } } private val queuedViewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val queuedEvents: LiveDataCollection = queuedViewEvents protected fun postQueuedEvent(event: QueuedOneShotEvent) { queuedViewEvents.postValue(event) } [9]

Slide 140

Slide 140 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val viewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::ActiveOnlySingleShotLiveData) val events: LiveDataCollection = viewEvents protected fun postEvent(event: OneShotEvent) { viewEvents.postValue(event) } } private val queuedViewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val queuedEvents: LiveDataCollection = queuedViewEvents protected fun postQueuedEvent(event: QueuedOneShotEvent) { queuedViewEvents.postValue(event) }

Slide 141

Slide 141 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val viewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::ActiveOnlySingleShotLiveData) val events: LiveDataCollection = viewEvents protected fun postEvent(event: OneShotEvent) { viewEvents.postValue(event) } } private val queuedViewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val queuedEvents: LiveDataCollection = queuedViewEvents protected fun postQueuedEvent(event: QueuedOneShotEvent) { queuedViewEvents.postValue(event) }

Slide 142

Slide 142 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val viewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::ActiveOnlySingleShotLiveData) val events: LiveDataCollection = viewEvents protected fun postEvent(event: OneShotEvent) { viewEvents.postValue(event) } } private val queuedViewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val queuedEvents: LiveDataCollection = queuedViewEvents protected fun postQueuedEvent(event: QueuedOneShotEvent) { queuedViewEvents.postValue(event) }

Slide 143

Slide 143 text

RainbowCake abstract class RainbowCakeViewModel( initialState: VS ) : ViewModel() { private val viewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::ActiveOnlySingleShotLiveData) val events: LiveDataCollection = viewEvents protected fun postEvent(event: OneShotEvent) { viewEvents.postValue(event) } } private val queuedViewEvents: MutableLiveDataCollection = MutableLiveDataCollectionImpl(::QueuedSingleShotLiveData) val queuedEvents: LiveDataCollection = queuedViewEvents protected fun postQueuedEvent(event: QueuedOneShotEvent) { queuedViewEvents.postValue(event) }

Slide 144

Slide 144 text

RainbowCake interface OneShotEvent interface QueuedOneShotEvent : OneShotEvent abstract class RainbowCakeFragment> : Fragment() { override fun onAttach(context: Context) { // ... } viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } })

Slide 145

Slide 145 text

RainbowCake interface OneShotEvent interface QueuedOneShotEvent : OneShotEvent abstract class RainbowCakeFragment> : Fragment() { override fun onAttach(context: Context) { // ... } protected open fun onEvent(event: OneShotEvent) {} } viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } })

Slide 146

Slide 146 text

RainbowCake interface OneShotEvent interface QueuedOneShotEvent : OneShotEvent abstract class RainbowCakeFragment> : Fragment() { override fun onAttach(context: Context) { // ... viewModel.queuedEvents.observe(this, Observer { event -> event?.let { onEvent(it) } }) } protected open fun onEvent(event: OneShotEvent) {} } viewModel.events.observe(this, Observer { event -> event?.let { onEvent(it) } })

Slide 147

Slide 147 text

ViewModel Fragment Fragment Fragment

Slide 148

Slide 148 text

ViewModel Fragment Fragment Fragment

Slide 149

Slide 149 text

ViewModel Fragment

Slide 150

Slide 150 text

But… these are all hacks!

Slide 151

Slide 151 text

RainbowCake rainbowcake/rainbowcake

Slide 152

Slide 152 text

RainbowCake rainbowcake/rainbowcake rainbowcake.dev

Slide 153

Slide 153 text

RainbowCake rainbowcake/rainbowcake

Slide 154

Slide 154 text

RainbowCake rainbowcake/rainbowcake

Slide 155

Slide 155 text

zsmb13 zsmb.co/talks

Slide 156

Slide 156 text

Handling View State and Events with RainbowCake zsmb.co/talks zsmb13 Márton Braun › Built on Jetpack › Single view state › Two event streams › Many LiveData hacks