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

Taming WebSocket with Scarlet

Taming WebSocket with Scarlet

Despite being a well-established standard for bidirectional persistent connection between client and server, WebSocket is complicated to set up on Android. When Tinder migrated to WebSocket for its realtime chat experience, they developed Scarlet: an extensible Kotlin library inspired by Retrofit that eliminates the boilerplate code required to handle data serialization and specify when to connect and retry. After this talk, you’ll be able to configure Scarlet using many of its plugins and integrate any WebSocket API in 10 minutes.

https://tech.gotinder.com/taming-websocket-with-scarlet/
https://github.com/Tinder/Scarlet
https://github.com/Tinder/StateMachine
#android #websocket

Zhixuan Lai

June 15, 2018
Tweet

More Decks by Zhixuan Lai

Other Decks in Programming

Transcript

  1. WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080');

    // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server', data); });
  2. WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080');

    // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server', data); });
  3. WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080');

    // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server', data); });
  4. WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080');

    // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server', data); });
  5. WebSocket // Create WebSocket connection. const socket = new WebSocket('ws://localhost:8080');

    // Connection opened socket.addEventListener('open', function (event) { socket.send('Hello Server!'); }); // Listen for messages socket.addEventListener('message', function (event) { console.log('Message from server’, event.data); });
  6. Bitcoin Trading • Buy low and sell high • React

    to market movement • Realtime bitcoin price
  7. GDAX Ticker REST API interface GdaxRESTApi { @GET("/products/{product-id}/ticker") fun getTicker():

    Flowable<Ticker> } val gdaxRESTApi = retrofit.create<GdaxRESTApi>()
  8. GDAX Ticker REST API interface GdaxRESTApi { @GET("/products/BTC-USD/ticker") fun getTicker():

    Flowable<Ticker> } val gdaxRESTApi = retrofit.create<GdaxRESTApi>()
  9. GDAX Ticker REST API interface GdaxRESTApi { @GET("/products/BTC-USD/ticker") fun getTicker():

    Flowable<Ticker> } val gdaxRESTApi = retrofit.create<GdaxRESTApi>()
  10. GDAX Ticker REST API interface GdaxRESTApi { @GET("/products/BTC-USD/ticker") fun getTicker():

    Flowable<Ticker> } val gdaxRESTApi = retrofit.create<GdaxRESTApi>() gdaxRESTApi.getTicker() .subscribe { Log.d("Bitcoin price is ${ticker.price} at ${ticker.time}") }
  11. GDAX Ticker REST API interface GdaxRESTApi { @GET("/products/BTC-USD/ticker") fun getTicker():

    Flowable<Ticker> } val gdaxRESTApi = retrofit.create<GdaxRESTApi>() gdaxRESTApi.getTicker() .subscribe { ticker -> Log.d("Bitcoin price is \$${ticker.price} at ${ticker.time}”) }
  12. GDAX Ticker REST API interface GdaxRESTApi { @GET("/products/BTC-USD/ticker") fun getTicker():

    Flowable<Ticker> } val gdaxRESTApi = retrofit.create<GdaxRESTApi>() gdaxRESTApi.getTicker() .subscribe { ticker -> Log.d("Bitcoin price is \$${ticker.price} at ${ticker.time}”) }
  13. GDAX Ticker REST API interface GdaxRESTApi { @GET("/products/BTC-USD/ticker") fun getTicker():

    Flowable<Ticker> } val gdaxRESTApi = retrofit.create<GdaxRESTApi>() gdaxRESTApi.getTicker() .subscribe { ticker -> Log.d("Bitcoin price is \$${ticker.price} at ${ticker.time}”) } // "Bitcoin price is $7600.01 at 2018-06-09T16:46:57.458Z”
  14. What’s wrong with WebSocket? • Difficult to set up •

    Boilerplate code • State management headache
  15. What’s wrong with WebSocket? • Difficult to set up •

    Boilerplate code • State management headache • Difficult to maintain
  16. What’s wrong with WebSocket? • Difficult to set up •

    Boilerplate code • State management headache • Difficult to maintain • Server API changes
  17. What’s wrong with WebSocket? • Difficult to set up •

    Boilerplate code • State management headache • Difficult to maintain • Server API changes • Tech stack migration
  18. GDAX WebSocket API • REST: request and response • WebSocket:

    bidirectional messages • Send Subscribe on connection open to stream Ticker
  19. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = scarlet.create<GdaxWebSocketService>()
  20. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = scarlet.create<GdaxWebSocketService>()
  21. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = scarlet.create<GdaxWebSocketService>()
  22. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = scarlet.create<GdaxWebSocketService>()
  23. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = scarlet.create<GdaxWebSocketService>()
  24. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = scarlet.create<GdaxWebSocketService>()
  25. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    // ... } data class Subscribe( val type: String = "subscribe", @Json(name = "product_ids") val productIds: List<String>, val channels: List<String> )
  26. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    // ... } data class Subscribe( val type: String, val productIds: List<String>, val channels: List<String> )
  27. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    // ... } data class Subscribe( // ... ) val BITCOIN_TICKER_SUBSCRIBE_MESSAGE = Subscribe( type = "subscribe", productIds = listOf("BTC-USD"), channels = listOf("ticker") )
  28. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    // ... } data class Subscribe( // ... ) val BITCOIN_TICKER_SUBSCRIBE_MESSAGE = Subscribe( type = "subscribe", productIds = listOf("BTC-USD"), channels = listOf("ticker") )
  29. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    // ... } data class Subscribe( // ... ) val BITCOIN_TICKER_SUBSCRIBE_MESSAGE = // ...
  30. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    // ... } data class Subscribe( // ... ) val BITCOIN_TICKER_SUBSCRIBE_MESSAGE = // ... val gdaxService = scarlet.create<GdaxWebSocketService>()
  31. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    // ... } data class Subscribe( // ... ) val BITCOIN_TICKER_SUBSCRIBE_MESSAGE = // ... val gdaxService = // ...
  32. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    // ... } data class Subscribe( // ... ) val BITCOIN_TICKER_SUBSCRIBE_MESSAGE = // ... val gdaxService = // ... gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE)
  33. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    // ... } data class Subscribe( // ... ) val BITCOIN_TICKER_SUBSCRIBE_MESSAGE = // ... val gdaxService = // ... gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE)
  34. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> }
  35. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeOnConnectionOpenedEvent( ): Flowable<WebSocket.OnConnectionOpen<*>> }
  36. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> }
  37. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> } gdaxService.observeOnConnectionOpenedEvent() .subscribe { gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE) }
  38. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> } gdaxService.observeOnConnectionOpenedEvent() .subscribe { gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE) }
  39. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> } gdaxService.observeOnConnectionOpenedEvent() .subscribe { gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE) }
  40. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> }
  41. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeTicker(): Flowable<Ticker> } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is ${ticker.price} at ${ticker.time}") }
  42. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeTicker(): Flowable<Ticker> } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is ${ticker.price} at ${ticker.time}") }
  43. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeTicker(): Flowable<Ticker> } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is \$${ticker.price} at ${ticker.time}”) }
  44. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeTicker(): Flowable<Ticker> } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is \$${ticker.price} at ${ticker.time}”) } // "Bitcoin price is $7600.01 at 2018-06-09T16:46:57.458Z"
  45. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeTicker(): Flowable<Ticker> } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is \$${ticker.price} at ${ticker.time}”) } // "Bitcoin price is $7600.01 at 2018-06-09T16:46:57.458Z" // "Bitcoin price is $7600.85 at 2018-06-09T16:46:58.033Z"
  46. WebSocket with Scarlet interface GdaxWebSocketService { // ... @Receive fun

    observeTicker(): Flowable<Ticker> } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is \$${ticker.price} at ${ticker.time}”) } // "Bitcoin price is $7600.01 at 2018-06-09T16:46:57.458Z" // "Bitcoin price is $7600.85 at 2018-06-09T16:46:58.033Z" // "Bitcoin price is $7601.62 at 2018-06-09T16:46:58.610Z”
  47. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> }
  48. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> }
  49. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = scarlet.create<GdaxWebSocketService>() gdaxService.observeOnConnectionOpenedEvent() .subscribe { gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE) } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is ${ticker.price} at ${ticker.time}") }
  50. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = // ... gdaxService.observeOnConnectionOpenedEvent() .subscribe { gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE) } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is ${ticker.price} at ${ticker.time}") }
  51. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = // ... gdaxService.observeOnConnectionOpenedEvent() .subscribe { gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE) } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is ${ticker.price} at ${ticker.time}") }
  52. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = // ... gdaxService.observeOnConnectionOpenedEvent() .subscribe { gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE) } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is ${ticker.price} at ${ticker.time}") }
  53. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = // ... gdaxService.observeOnConnectionOpenedEvent() .subscribe { gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE) } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is ${ticker.price} at ${ticker.time}") }
  54. WebSocket with Scarlet interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> } val gdaxService = // ... gdaxService.observeOnConnectionOpenedEvent() .subscribe { gdaxService.sendSubscribe(BITCOIN_TICKER_SUBSCRIBE_MESSAGE) } gdaxService.observeTicker() .subscribe { ticker -> Log.d("Bitcoin price is ${ticker.price} at ${ticker.time}") }
  55. Scarlet Plugins • Built-in with OkHttp, Moshi, Gson, Protobuf, RxJava

    • Roadmap: Jackson, xml, LiveData, Java 9 Flow, and more
  56. Scarlet Plugins • Built-in with OkHttp, Moshi, Gson, Protobuf, RxJava

    • Roadmap: Jackson, xml, LiveData, Java 9 Flow, and more • Fully customizable
  57. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  58. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  59. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  60. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  61. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  62. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  63. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  64. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  65. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  66. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  67. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  68. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  69. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  70. WebSocketFactory const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  71. WebSocketFactory const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(MockHttpServer().newWebSocketFactory()) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  72. WebSocketFactory const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  73. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  74. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  75. MessageAdapterFactory const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  76. MessageAdapterFactory const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(GsonMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  77. MessageAdapterFactory const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(ProtobufMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  78. MessageAdapterFactory (Roadmap) const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(JacksonMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  79. MessageAdapterFactory (Roadmap) const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(XmlMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  80. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(XmlMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  81. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(XmlMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  82. StreamAdapter • Stream • Sequence of asynchronous values • Scarlet

    Stream • Converts Scarlet Stream into other abstractions
  83. StreamAdapterFactory const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  84. StreamAdapterFactory const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava1StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  85. StreamAdapterFactory (Roadmap) const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(LiveDataStreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  86. StreamAdapterFactory (Roadmap) const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(Java9FlowStreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  87. StreamAdapterFactory (Roadmap) const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(KotlinCoroutineStreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  88. Scarlet Plugins const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(KotlinCoroutineStreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  89. Supporting API Changes interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> @Receive fun observeLevel2(): Flowable<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  90. Supporting API Changes interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> @Receive fun observeLevel2(): Flowable<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  91. Changing WebSocket Implementation interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> @Receive fun observeLevel2(): Flowable<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  92. Changing WebSocket Implementation interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> @Receive fun observeLevel2(): Flowable<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  93. Changing JSON Library interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> @Receive fun observeLevel2(): Flowable<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  94. Changing JSON Library interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> @Receive fun observeLevel2(): Flowable<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(JacksonMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  95. Supporting Kotlin Coroutine interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Flowable<Ticker> @Receive fun observeLevel2(): Flowable<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(JacksonMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  96. Supporting Kotlin Coroutine interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Sequence<Ticker> @Receive fun observeLevel2(): Sequence<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(JacksonMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  97. Supporting Kotlin Coroutine interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Sequence<Ticker> @Receive fun observeLevel2(): Sequence<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(JacksonMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  98. Supporting Kotlin Coroutine interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Sequence<Ticker> @Receive fun observeLevel2(): Sequence<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(JacksonMessageAdapter.Factory()) .addStreamAdapterFactory(KotlinCoroutineStreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  99. Supporting Kotlin Coroutine interface GdaxWebSocketService { @Send fun sendSubscribe(subscribe: Subscribe)

    @Receive fun observeOnConnectionOpenedEvent( ): Flowable<WebSocket.Event.OnConnectionOpen<*>> @Receive fun observeTicker(): Sequence<Ticker> @Receive fun observeLevel2(): Sequence<Level2> } val scarlet = Scarlet.Builder() .webSocketFactory(CustomWebSocketFactory()) .addMessageAdapterFactory(JacksonMessageAdapter.Factory()) .addStreamAdapterFactory(KotlinCoroutineStreamAdapter.Factory()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  100. Scarlet Reliability • Tested in production • 190 countries •

    26 million matches every day • Thousands of devices
  101. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  102. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  103. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  104. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  105. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  106. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  107. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  108. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  109. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  110. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  111. State Machine DSL sealed class State { object Solid :

    State() object Liquid : State() object Gas : State() } sealed class Event { object OnMelted : Event() object OnFroze : Event() object OnVaporized : Event() object OnCondensed : Event() }
  112. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  113. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  114. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  115. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  116. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  117. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  118. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  119. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  120. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  121. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  122. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  123. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  124. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  125. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  126. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  127. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  128. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  129. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  130. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  131. State Machine DSL StateMachine.create<State, Event> { initialState(Solid) state<Solid> { on<OnMelted>

    { transitionTo(Liquid) } } state<Liquid> { on<OnFroze> { transitionTo(Solid) } on<OnVaporized> { transitionTo(Gas) } } state<Gas> { on<OnCondensed> { transitionTo(Liquid) } } }
  132. Testing State Machine val stateMachine = StateMachine.create<State, Event> { //

    ... } assertThat(stateMachine.state).isEqualTo(Solid) // When stateMachine.transition(OnMelted) // Then assertThat(stateMachine.state).isEqualTo(Liquid)
  133. Testing State Machine val stateMachine = StateMachine.create<State, Event> { //

    ... } assertThat(stateMachine.state).isEqualTo(Solid) // When stateMachine.transition(OnMelted) // Then assertThat(stateMachine.state).isEqualTo(Liquid)
  134. Testing State Machine val stateMachine = StateMachine.create<State, Event> { //

    ... } assertThat(stateMachine.state).isEqualTo(Solid) // When stateMachine.transition(OnMelted) // Then assertThat(stateMachine.state).isEqualTo(Liquid)
  135. Testing State Machine val stateMachine = StateMachine.create<State, Event> { //

    ... } assertThat(stateMachine.state).isEqualTo(Solid) // When stateMachine.transition(OnMelted) // Then assertThat(stateMachine.state).isEqualTo(Liquid)
  136. Testing State Machine val stateMachine = StateMachine.create<State, Event> { //

    ... } assertThat(stateMachine.state).isEqualTo(Solid) // When stateMachine.transition(OnMelted) // Then assertThat(stateMachine.state).isEqualTo(Liquid)
  137. State Management Plugins • Lifecycle • Declare when to connect

    and to keep retrying • Backoff Strategy • Declare when to retry
  138. Scarlet • Easy to get started • Declarative API •

    Built-in plugins • Customizable • Easy to maintain
  139. Scarlet • Easy to get started • Declarative API •

    Built-in plugins • Customizable • Easy to maintain • Reliable
  140. Scarlet • Easy to get started • Declarative API •

    Built-in plugins • Customizable • Easy to maintain • Reliable • StateMachine DSL
  141. Scarlet • Easy to get started • Declarative API •

    Built-in plugins • Customizable • Easy to maintain • Reliable • StateMachine DSL • State management plugins
  142. Lifecycle const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(BACKOFF_STRATEGY) .lifecycle(createAppForegroundAndUserLoggedInLifecycle()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  143. Lifecycle const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(BACKOFF_STRATEGY) .lifecycle(AndroidLifecycle.ofApplicationForeground()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  144. Lifecycle const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(BACKOFF_STRATEGY) .lifecycle(AndroidLifecycle.ofApplicationForeground()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  145. Lifecycle const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(BACKOFF_STRATEGY) .lifecycle(createAppForegroundAndUserLoggedInLifecycle()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  146. Lifecycle const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet = Scarlet.Builder()

    .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(BACKOFF_STRATEGY) .lifecycle(createAppForegroundAndUserLoggedInLifecycle()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  147. Backoff Strategy const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(ExponentialWithJitterBackoffStrategy()) .lifecycle(createAppForegroundAndUserLoggedInLifecycle()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  148. Backoff Strategy const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(LinearBackoffStrategy()) .lifecycle(createAppForegroundAndUserLoggedInLifecycle()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  149. Backoff Strategy const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(ExponentialBackoffStrategy()) .lifecycle(createAppForegroundAndUserLoggedInLifecycle()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  150. Backoff Strategy const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(ExponentialWithJitterBackoffStrategy()) .lifecycle(createAppForegroundAndUserLoggedInLifecycle()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  151. Backoff Strategy const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(CustomBackoffStrategy()) .lifecycle(createAppForegroundAndUserLoggedInLifecycle()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()
  152. Backoff Strategy const val GDAX_URL = "wss://ws-feed.gdax.com" val scarlet =

    Scarlet.Builder() .webSocketFactory(okHttpClient.newWebSocketFactory(GDAX_URL)) .addMessageAdapterFactory(MoshiMessageAdapter.Factory()) .addStreamAdapterFactory(RxJava2StreamAdapter.Factory()) .backoffStrategy(CustomBackoffStrategy()) .lifecycle(createAppForegroundAndUserLoggedInLifecycle()) .build() val gdaxService = scarlet.create<GdaxWebSocketService>()