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

Using Square Workflow for Android & iOS

Using Square Workflow for Android & iOS

Mohit S

May 18, 2022
Tweet

More Decks by Mohit S

Other Decks in Programming

Transcript

  1. Mohit Sarveiya
    Using Square Workflow for Android & iOS
    @heyitsmohit

    View Slide

  2. Using Square Workflow for Android & iOS
    ● Purpose

    ● Building screens & setting up state

    ● Working with Compose

    View Slide

  3. Purpose
    ● Clear boundaries between components

    ● Immutable State

    ● Separation between state & UI

    View Slide

  4. Workflow

    View Slide

  5. Workflow

    (State)

    View Slide

  6. Workflow

    (State)
    Actions

    View Slide

  7. Workflow

    (State)
    Actions

    View Slide

  8. Workflow

    (State)
    Rendering

    View Slide

  9. Action
    UI

    View Slide

  10. Action
    UI
    State

    View Slide

  11. Action
    UI
    State
    Rendering

    View Slide

  12. Action
    UI
    State
    Rendering

    View Slide

  13. Creating Workflows
    ● State

    ● Actions

    ● Screens

    View Slide

  14. Square Workflow with Android
    Screens, Workflows & Renderings

    View Slide

  15. 9:41
    Login
    Username
    Password

    View Slide

  16. Workflow
    Layout Runner
    Screen

    View Slide

  17. Setup
    ● AndroidX View Model

    ● View Binding

    View Slide

  18. Screen
    ● Represents view model for screen

    View Slide

  19. Screen
    data class LoginScreen(

    val username: String,

    Val password: String,

    val onLoginClicked: ()
    ->
    Unit

    )

    View Slide

  20. Layout Runner
    ● Specifies how to update views

    ● Supports view binding

    View Slide

  21. Layout Runner
    class LoginLayoutRunner(

    val loginViewBinding: LoginViewBinding

    ) : LayoutRunner {

    }

    View Slide

  22. Layout Runner
    override fun showRendering(rendering: LoginScreen) {

    loginViewBinding.username.text = rendering.username

    loginViewBinding.username.setTextChangedListener {

    rendering.onUsernameChanged(it.toString())

    }

    loginViewBinding.login.setOnClickListener {

    rendering.onLoginClicked()

    }

    }

    View Slide

  23. Layout Runner
    override fun showRendering(rendering: LoginScreen) {

    loginViewBinding.username.text = rendering.username

    loginViewBinding.username.setTextChangedListener {

    rendering.onUsernameChanged(it.toString())

    }

    loginViewBinding.login.setOnClickListener {

    rendering.onLoginClicked()

    }

    }

    View Slide

  24. Layout Runner
    override fun showRendering(rendering: LoginScreen) {

    loginViewBinding.username.text = rendering.username

    loginViewBinding.username.setTextChangedListener {

    rendering.onUsernameChanged(it.toString())

    }

    loginViewBinding.login.setOnClickListener {

    rendering.onLoginClicked()

    }

    }

    View Slide

  25. Layout Runner
    override fun showRendering(rendering: LoginScreen) {

    loginViewBinding.username.text = rendering.username

    loginViewBinding.username.setTextChangedListener {

    rendering.onUsernameChanged(it.toString())

    }

    loginViewBinding.login.setOnClickListener {

    rendering.onLoginClicked()

    }

    }

    View Slide

  26. Workflow
    Layout Runner
    Screen

    View Slide

  27. Workflow

    (State)
    Rendering
    Actions

    View Slide

  28. data class State(

    val username: String,

    val password: String

    )

    State

    View Slide

  29. fun onUsernameChanged(username: String) = action {

    state = state.copy(username = username)

    }

    Action

    View Slide

  30. object LoginWorkflow : StatefulWorkflow {

    }
    Workflow

    View Slide

  31. object LoginWorkflow : StatefulWorkflow {

    override fun initialState() { }

    override fun render() { }

    }
    Workflow

    View Slide

  32. object LoginWorkflow : StatefulWorkflow {

    override fun initialState() { }

    override fun render() { }

    }
    Workflow

    View Slide

  33. fun render(renderState: State) {

    }
    Workflow

    View Slide

  34. fun render(renderState: State): LoginScreen {

    }
    Workflow

    View Slide

  35. fun render(renderState: State): LoginScreen {

    LoginScreen(

    username = renderState.username,

    onUsernameChanged = {
    ...
    },

    onLoginCliked = {}

    )

    }
    Workflow

    View Slide

  36. fun render(renderState: State): LoginScreen {

    LoginScreen(

    username = renderState.username,

    onUsernameChanged = {
    ...
    },

    onLoginCliked = {}

    )

    }
    Workflow

    View Slide

  37. fun onUsernameChanged(username: String) = action {

    state = state.copy(username = username)

    }

    Action

    View Slide

  38. fun render(renderState: State): LoginScreen {

    LoginScreen(

    username = renderState.username,

    onUsernameChanged = { context.actionSink.send() },

    onLoginCliked = {}

    )

    }
    Workflow

    View Slide

  39. fun render(renderState: State): LoginScreen {

    LoginScreen(

    username = renderState.username,

    onUsernameChanged = { context.actionSink.send(onUserChanged(it)) },
    onLoginCliked = {}

    )

    }
    Workflow

    View Slide

  40. fun initialState(): State {

    State(

    username = ""

    )

    }
    Workflow

    View Slide

  41. Workflow

    (State)
    Rendering
    Actions

    View Slide

  42. View & View Model
    View View Model Workflow

    View Slide

  43. View Model
    class LoginViewModel : ViewModel() {

    val renderings: StateFlow by lazy {

    renderWorkflowIn(

    workflow = LoginWorkflow,

    scope = viewModelScope,

    savedStateHandle = savedState

    )

    }

    }

    View Slide

  44. View Model
    class LoginViewModel : ViewModel() {

    val renderings: StateFlow by lazy {

    renderWorkflowIn(

    workflow = LoginWorkflow,

    scope = viewModelScope,

    savedStateHandle = savedState

    )

    }

    }

    View Slide

  45. View
    class LoginActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)


    val model: LoginViewModel by viewModels()

    setContentView(

    WorkflowLayout(this).apply { start(model.renderings) }

    )

    }

    }

    View Slide

  46. View
    class LoginActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)


    val model: LoginViewModel by viewModels()

    setContentView(

    WorkflowLayout(this).apply { start(model.renderings) }

    )

    }

    }

    View Slide

  47. View
    class LoginActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(

    WorkflowLayout(this).apply { start(model.renderings) }

    )

    }

    }

    View Slide

  48. Workflow
    Layout Runner
    Screen

    View Slide

  49. 9:41
    Login
    Username
    Password

    View Slide

  50. Action
    UI
    State
    Rendering

    View Slide

  51. Navigation & Props

    View Slide

  52. Workflow B
    Workflow A
    Props

    View Slide

  53. 9:41
    Login
    Username
    Password

    View Slide

  54. 9:41
    Login
    Username
    Password
    9:41
    Username

    View Slide

  55. Todo Workflow
    Login Workflow
    Root Workflow

    View Slide

  56. Root Workflow
    ● Navigation States

    ● Backstack

    View Slide

  57. sealed class State {

    object Login : State()

    }

    State

    View Slide

  58. sealed class State {

    object Login : State()

    data class Todo(val username: String) : State()

    }

    State

    View Slide

  59. Root Workflow
    object RootWorkflow : StatefulWorkflow {

    }

    View Slide

  60. Root Workflow
    object RootWorkflow : StatefulWorkflow {

    fun render(

    renderProps: Unit,

    renderState: State,

    context: RenderContext

    ): BackStackScreen

    }

    View Slide

  61. Root Workflow
    fun render(
    ...
    ): BackStackScreen {

    val backstackScreens = mutableListOf()

    }

    View Slide

  62. Root Workflow
    fun render(
    ...
    ): BackStackScreen {

    val loginScreen = context.renderChild(LoginWorkflow)

    }

    View Slide

  63. Root Workflow
    fun render(
    ...
    ): BackStackScreen {

    val loginScreen = context.renderChild(LoginWorkflow)

    backstackScreens += loginScreen

    }

    View Slide

  64. Root Workflow
    fun render(
    ...
    ): BackStackScreen {

    when (renderState) {

    is Todo
    ->
    {

    }

    }

    }

    View Slide

  65. Root Workflow
    fun render(
    ...
    ): BackStackScreen {

    when (renderState) {

    is Todo
    ->
    {

    val todoScreen = context.renderChild(TodoWorkflow)

    backstackScreens += todoScreen

    }

    }

    }

    View Slide

  66. Todo Workflow
    Login Workflow
    Root Workflow

    View Slide

  67. Todo List Screen
    data class TodoListScreen(

    val username: String,

    val todoTitles: List

    )

    View Slide

  68. Todo Workflow State
    data class State(

    val todos: List

    )

    View Slide

  69. Todo Workflow State
    fun initialState() = State(

    listOf(

    TodoModel(

    title = "Workout",

    note = "Workout"

    )

    )

    )

    View Slide

  70. Todo Workflow
    Login Workflow
    Props
    Username

    View Slide

  71. Props
    object TodoListWorkflow : StatefulWorkflow() {

    data class ListProps(val username: String)

    }

    View Slide

  72. Todo Workflow
    Login Workflow
    Root Workflow

    View Slide

  73. Root Workflow
    fun render(
    ...
    ): BackStackScreen {

    when (renderState) {

    is Todo
    ->
    {

    val todoScreen = context.renderChild(TodoWorkflow)

    backstackScreens += todoScreen

    }

    }

    }

    View Slide

  74. Root Workflow
    fun render(
    ...
    ): BackStackScreen {

    when (renderState) {

    is Todo
    ->
    {

    val todoScreen = context.renderChild(

    TodoWorkflow,

    ListProps(username = renderState.username)

    )

    }

    }

    }

    View Slide

  75. View Model
    View

    View Slide

  76. View Model
    class LoginViewModel : ViewModel() {

    val renderings: StateFlow by lazy {

    renderWorkflowIn(

    workflow = LoginWorkflow,

    scope = viewModelScope,

    savedStateHandle = savedState

    )

    }

    }

    View Slide

  77. View Model
    class LoginViewModel : ViewModel() {

    val renderings: StateFlow by lazy {

    renderWorkflowIn(

    workflow = RootWorkflow,

    scope = viewModelScope,

    savedStateHandle = savedState

    )

    }

    }

    View Slide

  78. Todo Layout Runner
    Login Layout Runner

    View Slide

  79. View Registry
    val viewRegistry = ViewRegistry(

    BackStackContainer,

    LoginLayoutRunner,

    TodoListLayoutRunner

    )

    View Slide

  80. View Registry
    setContentView(

    WorkflowLayout(this).apply {

    start(model.renderings, viewRegistry)

    }

    )

    View Slide

  81. Todo Workflow
    Login Workflow
    Root Workflow

    View Slide

  82. 9:41
    Login
    Username
    Password
    9:41
    Username

    View Slide

  83. Square Workflow with Jetpack Compose

    View Slide

  84. 9:41
    Hello
    9:41
    Goodbye

    View Slide

  85. Workflow
    Binding

    View Slide

  86. Rendering
    data class Rendering(

    val message: String,

    val onClick: ()
    ->
    Unit

    )

    View Slide

  87. Binding
    composeScreenViewFactory { rendering, _
    ->


    Text(

    rendering.message,

    modifier = Modifier

    .clickable(onClick = rendering.onClick)

    .fillMaxSize()

    .wrapContentSize(Alignment.Center)

    )

    }

    View Slide

  88. Binding
    composeScreenViewFactory { rendering, _
    ->


    Text(

    rendering.message,

    modifier = Modifier

    .clickable(onClick = rendering.onClick)

    .fillMaxSize()

    .wrapContentSize(Alignment.Center)

    )

    }

    View Slide

  89. State
    enum class State {

    Hello,

    Goodbye;

    fun theOtherState(): State = when (this) {

    Hello
    ->
    Goodbye

    Goodbye
    ->
    Hello

    }

    }

    View Slide

  90. Action
    val helloAction = action {

    state = state.theOtherState()

    }

    View Slide

  91. Workflow
    object HelloWorkflow : StatefulWorkflow {

    }

    View Slide

  92. Workflow
    object HelloWorkflow : StatefulWorkflow {

    override fun render(): Rendering = Rendering(

    message = renderState.name,

    onClick = { context.actionSink.send(helloAction) }

    )

    }

    View Slide

  93. Action
    UI
    State
    Rendering

    View Slide

  94. 9:41
    Hello
    9:41
    Goodbye

    View Slide

  95. View Slide

  96. View Slide

  97. Thank You!
    www.codingwithmohit.com
    @heyitsmohit

    View Slide