Server Driven UI Workflow

Server Driven UI Workflow

Video: https://youtu.be/eHGFpRj_3Vk

Deploying an Android app to the play store always contains some cost in it. Furthermore the user isn't always keen to download (again) our latest versioned app. How about doubling the cost of development with the iOS app into account (or Web)?
At Square, we came up with a system in which the server owns the workflow of the client. This means the client doesn't know before hand which screen, which image or text it's gonna show next.

In this talk, we'll:

- Know why Square ended up choosing this system.
- Discover how we built it.
- Look at the challenges, the pros and cons of such an architecture.
- Talk about how we want to alter it for the future.

Attendees to this talk will be expose to solutions for problems every company is facing but don't address.

05162bc961c3654218bf1839974a4f35?s=128

Benoît Quenaudon

April 24, 2019
Tweet

Transcript

  1. 2.
  2. 3.

    A

  3. 4.

    A

  4. 5.
  5. 6.
  6. 7.
  7. 8.

    A

  8. 9.
  9. 10.

    A

  10. 11.

    A

  11. 12.
  12. 13.

    A

  13. 14.
  14. 15.

    A

  15. 16.
  16. 17.

    A

  17. 18.
  18. 19.

    A

  19. 20.
  20. 21.

    A

  21. 22.
  22. 23.

    A

  23. 24.

    A

  24. 25.
  25. 26.

    A

  26. 27.
  27. 28.

    A

  28. 29.
  29. 30.
  30. 31.
  31. 32.

    A

  32. 33.

    A

  33. 34.

    A

  34. 35.
  35. 36.
  36. 39.
  37. 40.
  38. 41.
  39. 43.
  40. 44.

    message IdentityVerificationBlocker { optional bool requires_birthdate = 1; optional bool

    requires_full_ssn = 2; optional bool requires_last_four_ssn = 3; optional bool requires_full_name = 4; optional bool requires_address = 5; }
  41. 46.

    Server sends back ExecuteContractResponse if (response is ExecuteContractResponse) { return

    R.layout.status_result_view; } Pass ExecuteContractResponse#statusResult to the view
  42. 47.

    Server sends back ExecuteContractResponse if (response is ExecuteContractResponse) { return

    R.layout.status_result_view; } Pass ExecuteContractResponse#statusResult to the view
  43. 48.

    Server sends back ExecuteContractResponse if (response is ExecuteContractResponse) { return

    R.layout.status_result_view; } Pass ExecuteContractResponse#statusResult to the view
  44. 50.

    B C D message StatusResult {a enum Icon {b SUCCESS

    = 1; PENDING = 2; FAILURE = 3; ACTION_REQUIRED = 4; NONE = 5; }c optionaloIcon icon = 1; }d
  45. 51.

    a C D message StatusResult {a enum Icon {b SUCCESS

    = 1; PENDING = 2; FAILURE = 3; ACTION_REQUIRED = 4; NONE = 5; }c optionaloIcon icon = 1; optionalostring text = 2; }d
  46. 52.

    B a C message StatusResult {a enum Icon {b SUCCESS

    = 1; PENDING = 2; FAILURE = 3; ACTION_REQUIRED = 4; NONE = 5; }c optionaloIcon icon = 1; optionalostring text = 2; optionaloStatusResultButton primary_button = 3; optionaloStatusResultButton secondary_button = 4; }d
  47. 53.

    B a C message StatusResult {a enum Icon {b SUCCESS

    = 1; PENDING = 2; FAILURE = 3; ACTION_REQUIRED = 4; NONE = 5; }c optionaloIcon icon = 1; optionalostring text = 2; optionaloStatusResultButton primary_button = 3; optionaloStatusResultButton secondary_button = 4; }d message StatusResultButton { enumoButtonAction { HOME_SCREENz=z1; LINK_CARDz=z2; OPEN_URLz=z3; } optionalnButtonAction actionz=z1; optionalnstring textz=z2; optionalnstring urlz=z3; }
  48. 54.

    B a D message StatusResult {a enum Icon {b SUCCESS

    = 1; PENDING = 2; FAILURE = 3; ACTION_REQUIRED = 4; NONE = 5; }c optionaloIcon icon = 1; optionalostring text = 2; optionaloStatusResultButton primary_button = 3; optionaloStatusResultButton secondary_button = 4; optionalostring promo_textw=w5; }d
  49. 55.

    message StatusResult {a enum Icon {b SUCCESS = 1; PENDING

    = 2; FAILURE = 3; ACTION_REQUIRED = 4; NONE = 5; }c optionaloIcon icon = 1; optionalostring text = 2; optionaloStatusResultButton primary_button = 3; optionaloStatusResultButton secondary_button = 4; optionalostring promo_textw=w5; }d
  50. 57.
  51. 58.
  52. 62.

    message FormBlocker { oneof Element { ButtonElement button_element = 1;

    LocalImageElement local_image_element = 2; RemoteImageElement remote_image_element = 3; MoneyElement money_element = 4; SpacerElement spacer_element = 5; TextElement text_element = 6; CustomizedCardElement customized_card_element = 7; AddressElement address_element = 9; TextInputElement text_input_element = 10; OptionPickerElement option_picker_element = 11; DetailRowElement detail_row_element = 12; CurrencyConversionFlagsElement currency_conversion_flags_element = 13; } repeated Element elements = 1; optional BlockerAction primary_action = 2; optional BlockerAction secondary_action = 3; } ButtonElement LocalImageElement RemoteImageElement MoneyElement SpacerElement TextElement CustomizedCardElement AddressElement TextInputElement OptionPickerElement DetailRowElement CurrencyConversionFlagsElement
  53. 63.

    message FormBlocker { oneof Element { ButtonElement button_element = 1;

    LocalImageElement local_image_element = 2; RemoteImageElement remote_image_element = 3; MoneyElement money_element = 4; SpacerElement spacer_element = 5; TextElement text_element = 6; CustomizedCardElement customized_card_element = 7; AddressElement address_element = 9; TextInputElement text_input_element = 10; OptionPickerElement option_picker_element = 11; DetailRowElement detail_row_element = 12; CurrencyConversionFlagsElement currency_conversion_flags_element = 13; } repeated Element elements = 1; optional BlockerAction primary_action = 2; optional BlockerAction secondary_action = 3; }
  54. 64.
  55. 72.

    • Force update • Redirect to Web App • Smoke&Mirror

    usage of old blockers • Use fallback blocker • Don't send blocker - skip it
  56. 74.
  57. 75.

    • Real logic lives in only one platform • Lot

    of features/fixes don't involve clients at all • Client logic is simple - to some degree • Changes available without client updates