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

[Cesar Valiente] Unidirectional data-flow on Android using Kotlin

[Cesar Valiente] Unidirectional data-flow on Android using Kotlin

Presentation from GDG DevFest Ukraine 2017 - the biggest community-driven Google tech conference in the CEE.

Learn more at: https://devfest.gdg.org.ua

Google Developers Group Lviv

October 14, 2017
Tweet

More Decks by Google Developers Group Lviv

Other Decks in Technology

Transcript

  1. Flux and Redux Architectures based in unidirectional data flows. We

    start in A and we finish in A. Global app state. State has everything the App needs.
  2. ACTION REDUCER VIEW CONTROLLER-VIEW SIDE EFFECT 1 STORE Action State

    User interaction UI Data Optional Action SIDE EFFECT N
  3. ACTION REDUCER VIEW CONTROLLER-VIEW SIDE EFFECT 1 STORE Action State

    User interaction UI Data Optional Action SIDE EFFECT N
  4. ACTION REDUCER VIEW CONTROLLER-VIEW SIDE EFFECT 1 STORE Action State

    User interaction UI Data Optional Action SIDE EFFECT N
  5. ACTION REDUCER VIEW CONTROLLER-VIEW SIDE EFFECT 1 STORE Action State

    User interaction UI Data Optional Action SIDE EFFECT N
  6. ACTION REDUCER VIEW CONTROLLER-VIEW SIDE EFFECT 1 STORE Action State

    User interaction UI Data Optional Action SIDE EFFECT N
  7. ACTION REDUCER VIEW CONTROLLER-VIEW SIDE EFFECT 1 STORE Action State

    User interaction UI Data Optional Action SIDE EFFECT N
  8. ACTION REDUCER VIEW CONTROLLER-VIEW SIDE EFFECT 1 STORE Action State

    User interaction UI Data Optional Action SIDE EFFECT N
  9. ACTION REDUCER VIEW CONTROLLER-VIEW SIDE EFFECT 1 STORE Action State

    User interaction UI Data Optional Action SIDE EFFECT N
  10. NO FRAMEWORK DEPENDENCIES Dependencies ACTION REDUCER VIEW CONTROLLER-VIEW NO FRAMEWORK

    DEPENDENCIES SIDE EFFECT 1 ANDROID DEPENDENCIES SIDE EFFECT N STORE ANDROID DEPENDENCIES
  11. KUnidirectional OSS sample app which shows this architecture. A simple

    item list app. Everything is unidirectional.
  12. KUnidirectional OSS sample app which shows this architecture. A simple

    item list app. Everything is unidirectional. Without external libraries, just Kotlin + Android.
  13. View Are Activities and Fragments. It uses its ControllerView to

    start the process of what the user wants.
  14. View Are Activities and Fragments. It uses its ControllerView to

    start the process of what the user wants. It will render the data that comes from its ControllerView.
  15. ControllerView “Man in the middle” between UI and everything else.

    1 Activity/1Fragment -> 1 Controller-View. Creates and dispatches actions.
  16. ControllerView “Man in the middle” between UI and everything else.

    1 Activity/1Fragment -> 1 Controller-View. Creates and dispatches actions. Handles a new State.
  17. protected fun <T : Action> dispatch(action: T) {
 store.dispatch(action)
 }


    
 abstract fun handleState(state: State)
 } abstract class ControllerView(
 val store: Store,
 mainThread: ThreadExecutor? = null): LifecycleCallbacks, StateHandler(mainThread) {
  18. Action Are the intentions, what we want to do. They

    have metadata. They will be reduced or handled.
  19. sealed class Action
 sealed class UpdateAction : Action() {
 data

    class ReorderItemsAction(val items: List<Item>) : UpdateAction()
 
 data class UpdateItemAction(val localId: String,
 val text: String?,
 val color: Color) : UpdateAction()
 
 data class UpdateFavoriteAction( val localId: String, val favorite: Boolean) : UpdateAction()
 
 data class UpdateColorAction( val localId: String, val color: Color) : UpdateAction()
 } sealed class ReadAction : Action() {
 class FetchItemsAction : ReadAction()
 data class ItemsLoadedAction(val items: List<Item>) : ReadAction()
 }
  20. Store It will handle Actions. It will reduce an Action

    and dispatch a new State. It has the State of the app. State is immutable.
  21. Store It will handle Actions. It will reduce an Action

    and dispatch a new State. It has the State of the app. State is immutable. Dispatches an Action to its Side Effects after being reduced.
  22. State enum class Navigation {
 ITEMS_LIST,
 EDIT_ITEM
 }
 data class

    ItemsListScreen(
 val items: List<Item> = emptyList())
 
 data class EditItemScreen(val currentItem: Item = Item())
 
 data class State(
 val itemsListScreen: ItemsListScreen = ItemsListScreen(),
 val editItemScreen: EditItemScreen = EditItemScreen(),
 val navigation: Navigation = Navigation.ITEMS_LIST) Remember, State is immutable.
  23. Store abstract class Store( val sideEffects: CopyOnWriteArrayList<SideEffect> = CopyOnWriteArrayList(),
 val

    stateHandlers: CopyOnWriteArrayList<StateHandler> = CopyOnWriteArrayList(),
 val storeThread: ThreadExecutor? = null) : Subscribers { private fun handle(action: Action) {
 val newState = reduce(action, state)
 dispatch(newState)
 sideEffects.dispatch(action)
 } fun dispatch(newState: State) {
 state = newState
 stateHandlers.dispatch(state)
 } 
 . . .
  24. Store . . . private fun reduce(action: Action, currentState: State):

    State =
 when (action) {
 is CreationAction -> CreationReducer.reduce(action, currentState)
 is UpdateAction -> UpdateReducer.reduce(action, currentState)
 is ReadAction -> ReadReducer.reduce(action, currentState)
 is DeleteAction -> DeleteReducer.reduce(action, currentState)
 is NavigationAction -> NavigationReducer.reduce(action, currentState)
 } }
  25. Reducer Reduces an action to create a new state. Reduces

    every part of the state independently.
  26. Reducer Reduces an action to create a new state. Reduces

    every part of the state independently. The resulted new state will be dispatched.
  27. abstract class Reducer<in T : Action> {
 
 open fun

    reduce(action: T, currentState: State) : State =
 with(currentState) {
 currentState.copy(
 itemsListScreen = reduceItemsListScreen(action, itemsListScreen),
 editItemScreen = reduceEditItemScreen(action, editItemScreen),
 navigation = reduceNavigation(action, navigation)
 )
 } . . . }
  28. enum class Color {
 RED, YELLOW, GREEN, BLUE, WHITE
 }


    
 data class Item(
 val localId: String = generateLocalId(),
 val text: String? = null,
 val favorite: Boolean = false,
 val color: Color = Color.WHITE,
 val position: Long = object : PositionsFactory {}.newPosition()) Store Models
  29. import com.cesarvaliente.kunidirectional.persistence.Color as PersistenceColor
 import com.cesarvaliente.kunidirectional.persistence.Item as PersistenceItem
 import com.cesarvaliente.kunidirectional.store.Color

    as StoreColor
 import com.cesarvaliente.kunidirectional.store.Item as StoreItem Mapper fun StoreItem.toPersistenceItem(): PersistenceItem =
 with(this) {
 PersistenceItem(localId, text, favorite, color.toPersistenceColor(), position)
 }
 
 fun StoreColor.toPersistenceColor(): PersistenceColor =
 when (this) {
 StoreColor.BLUE -> PersistenceColor.BLUE
 StoreColor.GREEN -> PersistenceColor.GREEN
 StoreColor.RED -> PersistenceColor.RED
 StoreColor.WHITE -> PersistenceColor.WHITE
 StoreColor.YELLOW -> PersistenceColor.YELLOW
 }
  30. Presentation models? fun Color.toColorResource(): Int =
 when (this) {
 RED

    -> R.color.red
 YELLOW -> R.color.yellow
 GREEN -> R.color.green
 BLUE -> R.color.blue
 WHITE -> R.color.white
 }
 
 fun Item.getStableId(): Long = this.localId.hashCode().toLong()
  31. Side Effect We can have 0 or as many side

    effects as we want. Extends the functionality of your core logic.
  32. Side Effect We can have 0 or as many side

    effects as we want. It handles an Action right after the Store has reduced it. Extends the functionality of your core logic.
  33. Side Effect We can have 0 or as many side

    effects as we want. It handles an Action right after the Store has reduced it. It can return a new Action. Extends the functionality of your core logic.
  34. Testing? UI tests on Presentation layer (View). Unit + Integration

    tests on our ControllerViews. Unit + instrumentation tests in our Persistence layer (side effect).
  35. Testing? UI tests on Presentation layer (View). Unit + Integration

    tests on our ControllerViews. Unit + instrumentation tests in our Persistence layer (side effect). Unit tests in our Store layer.
  36. Demo 1! OPEN EDIT ITEM NAVIGATION UPDATED SAVE ITEM NEW

    ITEM FETCH LIST ITEMS LIST ITEMS Action State
  37. Demo 1! OPEN EDIT ITEM NAVIGATION UPDATED SAVE ITEM NEW

    ITEM TO NOTE LIST NAVIGATION UPDATED FETCH LIST ITEMS LIST ITEMS Action State
  38. Demo 1! OPEN EDIT ITEM NAVIGATION UPDATED SAVE ITEM NEW

    ITEM TO NOTE LIST NAVIGATION UPDATED FETCH LIST ITEMS LIST ITEMS FETCH LIST ITEMS LIST ITEMS Action State
  39. Demo 2! The cool thing of side effects in this

    architecture with decoupled elements.
  40. Demo 2! The cool thing of side effects in this

    architecture with decoupled elements.
  41. Final thoughts Unidirectional makes your code easy to follow. This

    is not the Philosopher Stone of the architectures.
  42. Final thoughts Decoupling elements in your app makes maintainability and

    testing easier. Unidirectional makes your code easy to follow. This is not the Philosopher Stone of the architectures.
  43. Final thoughts Decoupling elements in your app makes maintainability and

    testing easier. Unidirectional makes your code easy to follow. This is not the Philosopher Stone of the architectures. Immutability makes your code safer.
  44. Final thoughts Decoupling elements in your app makes maintainability and

    testing easier. Take advantage of the tools you have. Unidirectional makes your code easy to follow. This is not the Philosopher Stone of the architectures. Immutability makes your code safer.
  45. Final thoughts Decoupling elements in your app makes maintainability and

    testing easier. Take advantage of the tools you have. What is good for our problem maybe is not good for yours. Unidirectional makes your code easy to follow. This is not the Philosopher Stone of the architectures. Immutability makes your code safer.
  46. Useful Links KUnidirectional app KUnidirectional demo videos Flux Redux Luis

    G. Valle: Flux on Android André Staltz: Unidirectional data flow architectures Austin Mueller: Flux and Android Brian Egan and Guillaume Lung: Exploring the possibilities of Unidirectional Data Flow Architectures on Android
  47. License (cc) 2017 César Valiente. Some rights reserved. This document

    is distributed under the Creative Commons Attribution-ShareAlike 3.0 license, available in http://creativecommons.org/licenses/by-sa/3.0/
  48. Image licenses Flux and Redux images are property of Facebook.

    Emojis by Emoji One (CC-BY): http://emojione.com/