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

VIPER for Android

VIPER for Android

Adaptando e utilizando a arquitetura VIPER no Android.

Links:
- Android VIPER Templates
github.com/marciogranzotto/android-viper-templates
-Using the VIPER architecture on Android (Marcio Granzotto): ckl.io/blog/using-viper-architecture-android/
- iOS Project Architecture: Using VIPER (Pedro Peralta): ckl.io/blog/ios-project-architecture-using-viper/
- Single Responsibility Principle (Uncle Bob):
https://goo.gl/8Tbwg6
- Cheesecake Lab's Android Boilerplate
github.com/CheesecakeLabs/android-boilerplate

-----
Marcio Granzotto Rodrigues
[email protected]
https://github.com/marciogranzotto

GDG Floripa

May 17, 2017
Tweet

More Decks by GDG Floripa

Other Decks in Programming

Transcript

  1. What's VIPER? • Clean Architecture • Mainly used on iOS

    • Single Responsibility Principle • View Interactor Presenter Entity Router
  2. Why VIPER? • MVP ◦ Presenter and View have multiple

    responsibilities • Good architecture with well defined responsibilities • Great for tests • Same architecture can be used on iOS
  3. MVP + Interactor (VIPE) • Presenter ◦ handles UI events

    and prepares the data that comes from the Interactor to be displayed on the View • Interactor ◦ business logic and fetching models/entities from DBs or APIs • View ◦ handles UI and routing to another screens
  4. Contracts interface LoginContracts { interface View { fun presentHomeScreen(user: User)

    fun showError(message: String) } interface Presenter { fun onCreate(intent: Intent? = null) fun onDestroy() fun onLoginButtonPressed(username: String, password: String) } interface Interactor { fun unregister() fun login(username: String, password: String) } interface InteractorOutput { fun onLoginSuccess(user: User) fun onLoginError(message: String) } }
  5. View class LoginActivity: BaseActivity(), LoginContracts.View { var presenter: LoginContracts.Presenter? =

    null override fun onCreate() { //... presenter = LoginPresenter(this) presenter?.onCreate() loginButton.setOnClickListener { presenter?.onLoginButtonClicked(usernameEditText.text, passwordEditText.text) } } override fun onDestroy() { presenter?.onDestroy() presenter = null super.onDestroy() } fun presentHomeScreen(user: User) { val intent = Intent(view, HomeActivity::class.java) intent.putExtra(Constants.IntentExtras.USER, user) startActivity(intent) } fun showError(message: String) { /*shows the error on a dialog*/ } }
  6. Presenter class LoginPresenter(var view: LoginContracts.View?): LoginContracts.Presenter, LoginContracts.InteractorOutput { var interactor:

    LoginContracts.Interactor? = LoginInteractor(this) // Presenter: fun onCreate(intent: Intent?) { /* do something */} fun onDestroy() { view = null interactor?.unregister() interactor = null } fun onLoginButtonPressed(username: String, password: String) { interactor?.login(username, password) } // InteractorOutput: fun onLoginSuccess(user: User) { view?.presentNextScreen(user) } fun onLoginError(message: String) { view?.showError(message) } }
  7. Interactor class LoginInteractor(var output: LoginContracts.InteractorOutput?): LoginContracts.Interactor { fun unregister() {

    output = null } fun login(username: String, password: String) { LoginApiManager.login(username, password) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) ?.subscribe({ user -> //does something with the user, like saving it or the token output?.onLoginSuccess(user) }, { throwable -> output?.onLoginError(throwable.message ?: "Error!") }) } }
  8. What's the problem with that? • View ◦ handles UI

    and routing to another screens • Routing is a UI event? • Who's responsible to call system intents? ◦ Open camera ◦ Open external page
  9. VIPER • Presenter ◦ handles UI events and prepares the

    data that comes from the Interactor to be displayed on the View • Interactor ◦ business logic and fetching models/entities from DBs or APIs • View ◦ handles UI • Router ◦ routing to another screens
  10. Contracts interface LoginContracts { interface View { //fun presentHomeScreen(user: User)

    //This is no longer the View's responsibility fun getActivityContext(): Context //This is necessary to pass the activity to the router //... } //... interface Router { fun unregister() fun presentHomeScreen(user: User) //Now this is the Router's responsibility } }
  11. View class LoginActivity: BaseActivity(), LoginContracts.View { var presenter: LoginContracts.Presenter? =

    null fun getActivityContext(): Context { return this } // fun presentHomeScreen(user: User) { // val intent = Intent(view, HomeActivity::class.java) // intent.putExtra(Constants.IntentExtras.USER, user) // startActivity(intent) // } }
  12. Presenter class LoginPresenter(var view: LoginContracts.View?): LoginContracts.Presenter, LoginContracts.InteractorOutput { var interactor:

    LoginContracts.Interactor? = LoginInteractor(this) var router: LoginContracts.Router? = null fun onCreate(intent: Intent?) { val activity = view?.getActivityContext() as? Activity ?: return router = LoginRouter(activity) //... } fun onDestroy() { view = null interactor?.unregister() interactor = null } //... fun onLoginSuccess(user: User) { router?.presentNextScreen(user) } //... }
  13. Router class LoginRouter(var activity: Activity?): LoginContracts.Router { fun unregister() {

    activity = null } fun presentHomeScreen(user: User) { val intent = Intent(view, HomeActivity::class.java) intent.putExtra(Constants.IntentExtras.USER, user) activity?.startActivity(intent) } }
  14. Want to learn more? • Articles ◦ Using the VIPER

    architecture on Android (Marcio Granzotto): ckl.io/blog/using-viper-architecture-android/ ◦ iOS Project Architecture: Using VIPER (Pedro Peralta): ckl.io/blog/ios-project-architecture-using-viper/ ◦ Single Responsibility Principle (Uncle Bob): https://goo.gl/8Tbwg6 • Some code ◦ Cheesecake Lab's Android Boilerplate: github.com/CheesecakeLabs/android-boilerplate ▪ master-kotlin-viper Branch