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 full-size slide

  2. Using Square Workflow for Android & iOS
    ● Purpose

    ● Building screens & setting up state

    ● Working with Compose

    View full-size slide

  3. Purpose
    ● Clear boundaries between components

    ● Immutable State

    ● Separation between state & UI

    View full-size slide

  4. Workflow

    (State)

    View full-size slide

  5. Workflow

    (State)
    Actions

    View full-size slide

  6. Workflow

    (State)
    Actions

    View full-size slide

  7. Workflow

    (State)
    Rendering

    View full-size slide

  8. Action
    UI
    State

    View full-size slide

  9. Action
    UI
    State
    Rendering

    View full-size slide

  10. Action
    UI
    State
    Rendering

    View full-size slide

  11. Creating Workflows
    ● State

    ● Actions

    ● Screens

    View full-size slide

  12. Square Workflow with Android
    Screens, Workflows & Renderings

    View full-size slide

  13. 9:41
    Login
    Username
    Password

    View full-size slide

  14. Workflow
    Layout Runner
    Screen

    View full-size slide

  15. Setup
    ● AndroidX View Model

    ● View Binding

    View full-size slide

  16. Screen
    ● Represents view model for screen

    View full-size slide

  17. Screen
    data class LoginScreen(

    val username: String,

    Val password: String,

    val onLoginClicked: ()
    ->
    Unit

    )

    View full-size slide

  18. Layout Runner
    ● Specifies how to update views

    ● Supports view binding

    View full-size slide

  19. Layout Runner
    class LoginLayoutRunner(

    val loginViewBinding: LoginViewBinding

    ) : LayoutRunner {

    }

    View full-size slide

  20. 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 full-size slide

  21. 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 full-size 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 full-size 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 full-size slide

  24. Workflow
    Layout Runner
    Screen

    View full-size slide

  25. Workflow

    (State)
    Rendering
    Actions

    View full-size slide

  26. data class State(

    val username: String,

    val password: String

    )

    State

    View full-size slide

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

    state = state.copy(username = username)

    }

    Action

    View full-size slide

  28. object LoginWorkflow : StatefulWorkflow {

    }
    Workflow

    View full-size slide

  29. object LoginWorkflow : StatefulWorkflow {

    override fun initialState() { }

    override fun render() { }

    }
    Workflow

    View full-size slide

  30. object LoginWorkflow : StatefulWorkflow {

    override fun initialState() { }

    override fun render() { }

    }
    Workflow

    View full-size slide

  31. fun render(renderState: State) {

    }
    Workflow

    View full-size slide

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

    }
    Workflow

    View full-size slide

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

    LoginScreen(

    username = renderState.username,

    onUsernameChanged = {
    ...
    },

    onLoginCliked = {}

    )

    }
    Workflow

    View full-size slide

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

    LoginScreen(

    username = renderState.username,

    onUsernameChanged = {
    ...
    },

    onLoginCliked = {}

    )

    }
    Workflow

    View full-size slide

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

    state = state.copy(username = username)

    }

    Action

    View full-size slide

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

    LoginScreen(

    username = renderState.username,

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

    onLoginCliked = {}

    )

    }
    Workflow

    View full-size slide

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

    LoginScreen(

    username = renderState.username,

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

    )

    }
    Workflow

    View full-size slide

  38. fun initialState(): State {

    State(

    username = ""

    )

    }
    Workflow

    View full-size slide

  39. Workflow

    (State)
    Rendering
    Actions

    View full-size slide

  40. View & View Model
    View View Model Workflow

    View full-size slide

  41. View Model
    class LoginViewModel : ViewModel() {

    val renderings: StateFlow by lazy {

    renderWorkflowIn(

    workflow = LoginWorkflow,

    scope = viewModelScope,

    savedStateHandle = savedState

    )

    }

    }

    View full-size slide

  42. View Model
    class LoginViewModel : ViewModel() {

    val renderings: StateFlow by lazy {

    renderWorkflowIn(

    workflow = LoginWorkflow,

    scope = viewModelScope,

    savedStateHandle = savedState

    )

    }

    }

    View full-size slide

  43. 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 full-size slide

  44. 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 full-size slide

  45. View
    class LoginActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(

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

    )

    }

    }

    View full-size slide

  46. Workflow
    Layout Runner
    Screen

    View full-size slide

  47. 9:41
    Login
    Username
    Password

    View full-size slide

  48. Action
    UI
    State
    Rendering

    View full-size slide

  49. Navigation & Props

    View full-size slide

  50. Workflow B
    Workflow A
    Props

    View full-size slide

  51. 9:41
    Login
    Username
    Password

    View full-size slide

  52. 9:41
    Login
    Username
    Password
    9:41
    Username

    View full-size slide

  53. Todo Workflow
    Login Workflow
    Root Workflow

    View full-size slide

  54. Root Workflow
    ● Navigation States

    ● Backstack

    View full-size slide

  55. sealed class State {

    object Login : State()

    }

    State

    View full-size slide

  56. sealed class State {

    object Login : State()

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

    }

    State

    View full-size slide

  57. Root Workflow
    object RootWorkflow : StatefulWorkflow {

    }

    View full-size slide

  58. Root Workflow
    object RootWorkflow : StatefulWorkflow {

    fun render(

    renderProps: Unit,

    renderState: State,

    context: RenderContext

    ): BackStackScreen

    }

    View full-size slide

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

    val backstackScreens = mutableListOf()

    }

    View full-size slide

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

    val loginScreen = context.renderChild(LoginWorkflow)

    }

    View full-size slide

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

    val loginScreen = context.renderChild(LoginWorkflow)

    backstackScreens += loginScreen

    }

    View full-size slide

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

    when (renderState) {

    is Todo
    ->
    {

    }

    }

    }

    View full-size slide

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

    when (renderState) {

    is Todo
    ->
    {

    val todoScreen = context.renderChild(TodoWorkflow)

    backstackScreens += todoScreen

    }

    }

    }

    View full-size slide

  64. Todo Workflow
    Login Workflow
    Root Workflow

    View full-size slide

  65. Todo List Screen
    data class TodoListScreen(

    val username: String,

    val todoTitles: List

    )

    View full-size slide

  66. Todo Workflow State
    data class State(

    val todos: List

    )

    View full-size slide

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

    listOf(

    TodoModel(

    title = "Workout",

    note = "Workout"

    )

    )

    )

    View full-size slide

  68. Todo Workflow
    Login Workflow
    Props
    Username

    View full-size slide

  69. Props
    object TodoListWorkflow : StatefulWorkflow() {

    data class ListProps(val username: String)

    }

    View full-size slide

  70. Todo Workflow
    Login Workflow
    Root Workflow

    View full-size slide

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

    when (renderState) {

    is Todo
    ->
    {

    val todoScreen = context.renderChild(TodoWorkflow)

    backstackScreens += todoScreen

    }

    }

    }

    View full-size slide

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

    when (renderState) {

    is Todo
    ->
    {

    val todoScreen = context.renderChild(

    TodoWorkflow,

    ListProps(username = renderState.username)

    )

    }

    }

    }

    View full-size slide

  73. View Model
    View

    View full-size slide

  74. View Model
    class LoginViewModel : ViewModel() {

    val renderings: StateFlow by lazy {

    renderWorkflowIn(

    workflow = LoginWorkflow,

    scope = viewModelScope,

    savedStateHandle = savedState

    )

    }

    }

    View full-size slide

  75. View Model
    class LoginViewModel : ViewModel() {

    val renderings: StateFlow by lazy {

    renderWorkflowIn(

    workflow = RootWorkflow,

    scope = viewModelScope,

    savedStateHandle = savedState

    )

    }

    }

    View full-size slide

  76. Todo Layout Runner
    Login Layout Runner

    View full-size slide

  77. View Registry
    val viewRegistry = ViewRegistry(

    BackStackContainer,

    LoginLayoutRunner,

    TodoListLayoutRunner

    )

    View full-size slide

  78. View Registry
    setContentView(

    WorkflowLayout(this).apply {

    start(model.renderings, viewRegistry)

    }

    )

    View full-size slide

  79. Todo Workflow
    Login Workflow
    Root Workflow

    View full-size slide

  80. 9:41
    Login
    Username
    Password
    9:41
    Username

    View full-size slide

  81. Square Workflow with Jetpack Compose

    View full-size slide

  82. 9:41
    Hello
    9:41
    Goodbye

    View full-size slide

  83. Workflow
    Binding

    View full-size slide

  84. Rendering
    data class Rendering(

    val message: String,

    val onClick: ()
    ->
    Unit

    )

    View full-size slide

  85. Binding
    composeScreenViewFactory { rendering, _
    ->


    Text(

    rendering.message,

    modifier = Modifier

    .clickable(onClick = rendering.onClick)

    .fillMaxSize()

    .wrapContentSize(Alignment.Center)

    )

    }

    View full-size slide

  86. Binding
    composeScreenViewFactory { rendering, _
    ->


    Text(

    rendering.message,

    modifier = Modifier

    .clickable(onClick = rendering.onClick)

    .fillMaxSize()

    .wrapContentSize(Alignment.Center)

    )

    }

    View full-size slide

  87. State
    enum class State {

    Hello,

    Goodbye;

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

    Hello
    ->
    Goodbye

    Goodbye
    ->
    Hello

    }

    }

    View full-size slide

  88. Action
    val helloAction = action {

    state = state.theOtherState()

    }

    View full-size slide

  89. Workflow
    object HelloWorkflow : StatefulWorkflow {

    }

    View full-size slide

  90. Workflow
    object HelloWorkflow : StatefulWorkflow {

    override fun render(): Rendering = Rendering(

    message = renderState.name,

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

    )

    }

    View full-size slide

  91. Action
    UI
    State
    Rendering

    View full-size slide

  92. 9:41
    Hello
    9:41
    Goodbye

    View full-size slide

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

    View full-size slide