VIEW STATE MACHINE FOR NETWORK CALLS ON ANDROID @MANDYBESS
THOUGHTBOT
WHAT IS A VIEW STATE MACHINE?
!
BUILD AN APP TOGETHER ! "
GAME OF CONES !
MODELING !"!
ICE CREAM - title - icon
data class IceCream(val title: String, val iconUrl: String)
! ➡ "
MVP MODEL VIEW PRESENTER
VIEW INTERFACE interface MainView { fun showTitle(title: String) fun showIcon(url: String) }
PRESENTER class MainPresenter(val view: MainView) { fun onCreate() { val iceCream = ?? view.showTitle(iceCream.title) view.showIcon(iceCream.iconUrl) } }
DATA STORE interface DataStore { fun fetchCone(): IceCream }
PRESENTER + DATA STORE + class MainPresenter(val view: MainView, val dataStore: DataStore) { fun onCreate() { + val iceCream = dataStore.fetchCone() view.showTitle(iceCream.title) view.showIcon(iceCream.iconUrl) } }
TESTING !
PRESENTER TEST class MainPresenterTest { val view = mock<MainView>() val dataStore = mock<DataStore>() val fakeIceCream = IceCream("Vanilla", "www.icecream.com") @Test fun test_onCreate() { //stub response whenever(dataStore.fetchCone()).thenReturn(fakeIceCream) val presenter = MainPresenter(view, dataStore) presenter.onCreate() verify(view).showTitle("Vanilla") verify(view).showIcon("www.icecream.com") verifyNoMoreInteractions(view) }
PRESENTER TEST class MainPresenterTest { val view = mock<MainView>() val dataStore = mock<DataStore>() val fakeIceCream = IceCream("Vanilla", "www.icecream.com") @Test fun test_onCreate() { //stub response whenever(dataStore.fetchCone()).thenReturn(fakeIceCream) val presenter = MainPresenter(view, dataStore) presenter.onCreate() verify(view).showTitle("Vanilla") verify(view).showIcon("www.icecream.com") verifyNoMoreInteractions(view) }
PRESENTER TEST class MainPresenterTest { val view = mock<MainView>() val dataStore = mock<DataStore>() val fakeIceCream = IceCream("Vanilla", "www.icecream.com") @Test fun test_onCreate() { //stub response whenever(dataStore.fetchCone()).thenReturn(fakeIceCream) val presenter = MainPresenter(view, dataStore) presenter.onCreate() verify(view).showTitle("Vanilla") verify(view).showIcon("www.icecream.com") verifyNoMoreInteractions(view) }
PRESENTER TEST class MainPresenterTest { val view = mock<MainView>() val dataStore = mock<DataStore>() val fakeIceCream = IceCream("Vanilla", "www.icecream.com") @Test fun test_onCreate() { //stub response whenever(dataStore.fetchCone()).thenReturn(fakeIceCream) val presenter = MainPresenter(view, dataStore) presenter.onCreate() verify(view).showTitle("Vanilla") verify(view).showIcon("www.icecream.com") verifyNoMoreInteractions(view) }
PRESENTER TEST class MainPresenterTest { val view = mock<MainView>() val dataStore = mock<DataStore>() val fakeIceCream = IceCream("Vanilla", "www.icecream.com") @Test fun test_onCreate() { //stub response whenever(dataStore.fetchCone()).thenReturn(fakeIceCream) val presenter = MainPresenter(view, dataStore) presenter.onCreate() verify(view).showTitle("Vanilla") verify(view).showIcon("www.icecream.com") verifyNoMoreInteractions(view) }
PRESENTER TEST class MainPresenterTest { val view = mock<MainView>() val dataStore = mock<DataStore>() val fakeIceCream = IceCream("Vanilla", "www.icecream.com") @Test fun test_onCreate() { //stub response whenever(dataStore.fetchCone()).thenReturn(fakeIceCream) val presenter = MainPresenter(view, dataStore) presenter.onCreate() verify(view).showTitle("Vanilla") verify(view).showIcon("www.icecream.com") verifyNoMoreInteractions(view) }
PRESENTER TEST class MainPresenterTest { val view = mock<MainView>() val dataStore = mock<DataStore>() val fakeIceCream = IceCream("Vanilla", "www.icecream.com") @Test fun test_onCreate() { //stub response whenever(dataStore.fetchCone()).thenReturn(fakeIceCream) val presenter = MainPresenter(view, dataStore) presenter.onCreate() verify(view).showTitle("Vanilla") verify(view).showIcon("www.icecream.com") verifyNoMoreInteractions(view) }
NETWORK CALLS !"
DATA STORE + RX interface DataStore { + fun fetchCone(): Observable<IceCream> }
PRESENTER + RX class MainPresenter(val view: MainView, val dataStore: DataStore) { fun onCreate() { dataStore.fetchCone() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { iceCream -> // on success view.showTitle(iceCream.title) view.showIcon(iceCream.iconUrl) }, { error -> // on error } ) } }
PRESENTER + RX class MainPresenter(val view: MainView, val dataStore: DataStore) { fun onCreate() { dataStore.fetchCone() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { iceCream -> // on success view.showTitle(iceCream.title) view.showIcon(iceCream.iconUrl) }, { error -> // on error } ) } }
PRESENTER + RX class MainPresenter(val view: MainView, val dataStore: DataStore) { fun onCreate() { dataStore.fetchCone() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { iceCream -> // on success view.showTitle(iceCream.title) view.showIcon(iceCream.iconUrl) }, { error -> // on error } ) } }
PRESENTER + RX class MainPresenter(val view: MainView, val dataStore: DataStore) { fun onCreate() { dataStore.fetchCone() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { iceCream -> // on success view.showTitle(iceCream.title) view.showIcon(iceCream.iconUrl) }, { error -> // on error } ) } }
PRESENTER + RX class MainPresenter(val view: MainView, val dataStore: DataStore) { fun onCreate() { // loading?? dataStore.fetchCone() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { iceCream -> // on success view.showTitle(iceCream.title) view.showIcon(iceCream.iconUrl) }, { error -> // on error } ) } }
VIEW INTERFACE + NETWORKING interface MainView { fun showTitle(title: String) fun showIcon(url: String) + fun showLoading() + fun hideLoading() + fun showError(errorMessage: String) }
PRESENTER + NETWORKING class MainPresenter(val view: MainView, val dataStore: DataStore) { fun onCreate() { + view.showLoading() dataStore.fetchCone() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { iceCream -> + view.hideLoading() view.showTitle(iceCream.title) view.showIcon(iceCream.iconUrl) }, { error -> + view.hideLoading() + view.showError(error.message) } ) } }
PRESENTER TEST + RX + NETWORKING ... @Test fun test_onCreate_success() { // stub response - whenever(dataStore.fetchCone()).thenReturn(fakeIceCream) + whenever(dataStore.fetchCone()).thenReturn(Observable.just(fakeIceCream)) val presenter = MainPresenter(view, dataStore) presenter.onCreate() + verify(view).showLoading() + verify(view).hideLoading() verify(view).showTitle("Vanilla") verify(view).showIcon("www.icecream.com") verifyNoMoreInteractions(view) }
PRESENTER TEST + RX + NETWORKING ... @Test fun test_onCreate_error() { // stub response val errorMessage = "There was an error" whenever(dataStore.fetchCone()).thenReturn(Observable.error(Throwable(errorMessage))) val presenter = MainPresenter(view, dataStore) presenter.onCreate() verify(view).showLoading() verify(view).hideLoading() verify(view).showError(errorMessage) verifyNoMoreInteractions(view) }
WE DID IT !
!
UPDATES 1. Update our model 2. Update our view interface 3. Update our presenter 4. Update presenter tests
MODEL UPDATE data class IceCream(val title: String, val iconUrl: String, val calorieCount: Int)
VIEW INTERFACE + CALORIES interface MainView { fun showTitle(title: String) fun showIcon(url: String) fun showLoading() fun hideLoading() fun showError(errorMessage: String) + fun showCalorieCount(calorieCount: String) }
PRESENTER + CALORIES class MainPresenter(val view: MainView, val dataStore: DataStore) { fun onCreate() { view.showLoading() dataStore.fetchCone() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { iceCream -> view.hideLoading() view.showTitle(iceCream.title) view.showIcon(iceCream.iconUrl) + view.showCalorieCount(context.getString(R.string.formatted_calorie_count, iceCream.calorieCount)) }, { error -> view.hideLding() view.showError(error.message) } ) } }
Recommend
More recommend