Slide 1

Slide 1 text

Time Travel Debug Everything! From state machines to reactive subscriptions

Slide 2

Slide 2 text

Zsolt Kocsi Android engineer @ Badoo @ZsoltKocsi

Slide 3

Slide 3 text

Additional reading at the end of presentation

Slide 4

Slide 4 text

What is time travel debugging?

Slide 5

Slide 5 text

What is time travel debugging? 1 2 3 4 You can record and replay execution Not a video recording! Executing the same actions with the same data again Interactive – you can look under the hood

Slide 6

Slide 6 text

Time Travel Debugging = Reducer

Slide 7

Slide 7 text

Architectural overview Time travel debugger @ Badoo

Slide 8

Slide 8 text

Key elements https://github.com/badoo/MVICore

Slide 9

Slide 9 text

Unidirectional Reactive MVI

Slide 10

Slide 10 text

Architectural overview Unidirectional Reactive MVI

Slide 11

Slide 11 text

View State Store Architectural overview Unidirectional Reactive MVI

Slide 12

Slide 12 text

E →I S→VM State
 Store Analytics View Architectural overview Unidirectional Reactive MVI

Slide 13

Slide 13 text

E →I S→VM State
 Store Analytics View Architectural overview Unidirectional Reactive MVI

Slide 14

Slide 14 text

Architectural overview Unidirectional Reactive MVI E →I S→VM State
 Store Analytics View

Slide 15

Slide 15 text

val output: ObservableSource = Observable.just( "item1", "item2", "item3" )£ val input: Consumer = Consumer { System.out.println(it) }£ val disposable = Observable .wrap(output) .subscribe(input) E →I S→VM State
 Store AT View

Slide 16

Slide 16 text

val output: ObservableSource = Observable.just( "item1", "item2", "item3" )£ val input: Consumer = Consumer { System.out.println(it) }£ val binder = Binder() binder.bind(output to input) E →I S→VM State
 Store AT View

Slide 17

Slide 17 text

val output: ObservableSource = Observable.just( "item1", "item2", "item3" )£ val input: Consumer<> = Consumer { System.out.println(it) }£ val binder = Binder() binder.bind(output to input) E →I S→VM State
 Store AT View

Slide 18

Slide 18 text

val output: ObservableSource = Observable.just( "item1", "item2", "item3" )£ val input: Consumer = Consumer { System.out.println(it) }£ val binder = Binder() binder.bind(output to input) E →I S→VM State
 Store AT View

Slide 19

Slide 19 text

val output: ObservableSource = Observable.just( "item1", "item2", "item3" )££ val input: Consumer = Consumer { System.out.println(it) }£ val transformer : (String) -> Int = { it.length }£ val binder = Binder() binder.bind(output to input) E →I S→VM State
 Store AT View

Slide 20

Slide 20 text

val output: ObservableSource = Observable.just( "item1", "item2", "item3" )£ val input: Consumer = Consumer { System.out.println(it) }£ val transformer : (String) -> Int = { it.length }£ val binder = Binder() binder.bind(output to input using transformer) E →I S→VM State
 Store AT View

Slide 21

Slide 21 text

binder.bind(view to analyticsTracker) binder.bind(view to store using EventToIntent) binder.bind(store to view using StateToViewModel) // Binder acts as: // // CompositeDisposable // + syntactical sugar // + lifecycle aware // + not tied to Android! E →I S→VM State
 Store AT View

Slide 22

Slide 22 text

Architectural overview Unidirectional Reactive MVI E →I S→VM State
 Store Analytics View

Slide 23

Slide 23 text

Architectural overview Unidirectional Reactive MVI E →I S→VM State
 Store Analytics View

Slide 24

Slide 24 text

Architectural overview Unidirectional Reactive MVI E →I S→VM Analytics View Feature

Slide 25

Slide 25 text

Reducer Feature Architectural overview Unidirectional Reactive MVI Consumer ObservableSource

Slide 26

Slide 26 text

Architectural overview Unidirectional Reactive MVI Reducer Feature

Slide 27

Slide 27 text

data class State( val counter: Int = 0, val someText: String = "" )£ sealed class Wish { object IncreaseCounter : Wish() data class SetText(val text: String) : Wish() }• class ReducerImpl : Reducer { }¢ R Fe

Slide 28

Slide 28 text

data class State( val counter: Int = 0, val someText: String = "" )£ sealed class Wish { object IncreaseCounter : Wish() data class SetText(val text: String) : Wish() }• class ReducerImpl : Reducer { override fun invoke(state: State, wish: Wish): State = }¢ R Fe

Slide 29

Slide 29 text

data class State( val counter: Int = 0, val someText: String = "" )£ sealed class Wish { object IncreaseCounter : Wish() data class SetText(val text: String) : Wish() }• class ReducerImpl : Reducer { override fun invoke(state: State, wish: Wish): State = when (wish) { }™ }¢ R Fe

Slide 30

Slide 30 text

data class State( val counter: Int = 0, val someText: String = "" )£ sealed class Wish { object IncreaseCounter : Wish() data class SetText(val text: String) : Wish() }• class ReducerImpl : Reducer { override fun invoke(state: State, wish: Wish): State = when (wish) { IncreaseCounter -> state.copy( counter = state.counter + 1 )§ }™ }¢ R Fe

Slide 31

Slide 31 text

data class State( val counter: Int = 0, val someText: String = "" )£ sealed class Wish { object IncreaseCounter : Wish() data class SetText(val text: String) : Wish() }• class ReducerImpl : Reducer { override fun invoke(state: State, wish: Wish): State = when (wish) { IncreaseCounter -> state.copy( counter = state.counter + 1 )§ is SetText -> state.copy( someText = wish.text )¡ }™ }¢ R Fe

Slide 32

Slide 32 text

R Fe class SimpleFeature : ReducerFeature( initialState = State(), reducer = ReducerImpl() ) { data class State( val counter: Int = 0, val someText: String = "" )£ sealed class Wish { object IncreaseCounter : Wish() data class SetText(val text: String) : Wish() }• class ReducerImpl : Reducer { override fun invoke(state: State, wish: Wish): State = when (wish) { IncreaseCounter -> state.copy( counter = state.counter + 1 )§ is SetText -> state.copy( someText = wish.text )¡ }™ }¢ }

Slide 33

Slide 33 text

Architectural overview Unidirectional Reactive MVI Reducer Feature

Slide 34

Slide 34 text

Reducer Feature Architectural overview Unidirectional Reactive MVI

Slide 35

Slide 35 text

Reducer Feature Architectural overview Unidirectional Reactive MVI

Slide 36

Slide 36 text

Back to the… point

Slide 37

Slide 37 text

Reducer Feature Time travel debugger applicable to Feature components

Slide 38

Slide 38 text

Time travel debugger applicable to Reactive bindings E →I S→VM State
 Store Analytics View

Slide 39

Slide 39 text

Demo time!

Slide 40

Slide 40 text

Demo

Slide 41

Slide 41 text

Demo

Slide 42

Slide 42 text

Demo

Slide 43

Slide 43 text

Demo

Slide 44

Slide 44 text

Demo

Slide 45

Slide 45 text

Demo

Slide 46

Slide 46 text

Demo

Slide 47

Slide 47 text

Demo

Slide 48

Slide 48 text

Demo

Slide 49

Slide 49 text

Demo

Slide 50

Slide 50 text

Demo

Slide 51

Slide 51 text

Demo

Slide 52

Slide 52 text

Demo

Slide 53

Slide 53 text

Demo

Slide 54

Slide 54 text

Demo View News
 Listener Analytics Feature
 2 Feature
 1

Slide 55

Slide 55 text

binder.bind(view to feature1 using EventToWish1) binder.bind(view to feature2 using EventToWish2) binder.bind(view to analyticsTracker) binder.bind(feature2.news to newsListener) val combinedState = combineLatest(feature1, feature2) binder.bind(combinedState to view using StateToViewModel) View NL AT F2 F1

Slide 56

Slide 56 text

View NL AT F2 F1

Slide 57

Slide 57 text

Demo E→W2 E→W1 (S1,S2) →VM View News
 Listener Analytics Feature 2 Feature
 1

Slide 58

Slide 58 text

Demo E→W2 E→W1 (S1,S2) →VM View News
 Listener Analytics Feature 2 Feature
 1

Slide 59

Slide 59 text

Demo E→W2 E→W1 (S1,S2) →VM View News
 Listener Analytics Feature 2 Feature
 1

Slide 60

Slide 60 text

Demo E→W2 E→W1 (S1,S2) →VM View News
 Listener Analytics Feature 2 Feature
 1

Slide 61

Slide 61 text

Demo E→W2 E→W1 (S1,S2) →VM View News
 Listener Analytics Feature 2 Feature
 1

Slide 62

Slide 62 text

Additional resources Article about Features https://badootech.badoo.com/a-modern-kotlin-based-mvi- architecture-9924e08efab1 Article about Binder https://badootech.badoo.com/building-a-system-of-reactive- components-with-kotlin-ff56981e92cf GitHub project https://github.com/badoo/MVICore Londroid meetup December ‘18

Slide 63

Slide 63 text

github.com/zsoltk @ZsoltKocsi medium.com/@zsolt.kocsi linkedin.com/in/zsolt-kocsi #MVICore Thank you!