Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The MVI Architecture: A Reactive Approach to Android Development

The MVI Architecture: A Reactive Approach to Android Development

Adora Nwodo

July 27, 2019
Tweet

More Decks by Adora Nwodo

Other Decks in Programming

Transcript

  1. The MVI Architecture: A Reactive Approach to Android Development Developing

    Android Developers 3.0 Saturday 27th July, 2019
  2. LET’S TALK ABOUT A DIFFERENT ARCHITECTURE PATTERN Model View Intent

    Inspired by the unidirectional & cyclic nature of Cycle.js 6
  3. 14 A TYPICAL USER MODEL data class User( var id:

    Int?? = null, var firstName: String? = null, var lastName: String? = null, var birthday: String? = null, var bvn: String? = null, var email: String? = null )
  4. 15 USER PRESENTER IN MVP PATTERN class UserPresenter(private var view:

    UserView?) { override fun onViewCreated () { view.showLoadScreen() val user = loadUser() this.onFetchSuccess(user) } override fun onFetchSuccess (user: User) { view.hideLoadScreen() view.displayUser( user) } }
  5. 16 data class UserViewState { val loading: Boolean = false

    val user: User? = null val error: String? = null } WHAT IF WE COULD ALSO REPRESENT STATE?
  6. 17 sealed class UserResult { object Loading: UserResult() data class

    Success(val user: User) : UserResult() data class Error(val errorMsg: String) : UserResult() } OUR RESULTS
  7. 18 class UserViewModel @Inject constructor (dispatcher: UserDispatcher ): ViewModel() {

    private val state = UserViewState () val userState: LiveData<UserViewState> = Transformation. map(dispatcher.dispatchGetUser()){ when(it){ is UserResult.Loading -> state.copy(loading = true) is UserResult.Success -> state.copy(loading = false, user = it.data) is UserResult.Error -> state.copy(loading = false, error = “Could not fetch user”) } } } OUR VIEWMODEL
  8. 19 class UserDispatcher @Inject constructor (private val repository: UserRepository ){

    fun dispatchgetUser () = liveData{ emit(UserResult.Loading ) emit(getUser()) } private suspend fun getUser(): UserResult { try { val response = repository.getUser().await() if (response.isSuccessful) { response.body()?. let { responseBody -> responseBody.results?. run { return UserResult.Success (this)} } } } catch(t: Throwable){ return UserResult.Error (t.localizedMessage ) } return UserResult.Error (“Could not fetch user” ) } } DISPATCH OUR ACTION
  9. 20 override fun onViewCreated(view: View, savedInstanceBundle: Bundle?) = liveData{ super.onViewCreated(view,savedInstanceBundle)

    initViews() viewModel.userState.observe(this) { render(it) } } private fun render(state: UserViewState){ when(state) { is UserViewState.loading -> renderLoadingView() is UserViewState.user -> renderUserView(state) is UserViewState.error -> renderErrorView(state) } } private fun renderLoadingView(){ // Code here } private fun renderUserView(userState: UserViewState.user){ // Code here } private fun renderErrorView(errorState: UserViewState.error){ // Code here } WHAT OUR VIEW LOOKS LIKE
  10. WHY USE MVI? • A unidirectional & cyclical flow •

    Immutable models that provide consistent behavior & thread safety on big apps • Independent UI components • No callbacks 22
  11. COMPARING MVI TO MVVM • MVI uses reactive programming while

    MVVM uses imperative programming • Models in MVI represent state and MVVM represents data 23
  12. SUMMARY • The MVI Architecture • Reactive programming • How

    components interact • MVI with Coroutines & LiveData • Why used MVI? • Comparing MVI to MVVM 24