Slide 1

Slide 1 text

In the name of God Operation Flow

Slide 2

Slide 2 text

I am Seyed Jafari Senior Software Engineer @ Revolut Ltd worldsnas.com @worldsnas

Slide 3

Slide 3 text

What is Flow Docs: An asynchronous data stream that sequentially emits values and completes normally or with an exception.

Slide 4

Slide 4 text

Everything is a stream Image from reactiveStreams: https://gist.github.com/staltz/868e7e9bc2a7b8c1f754

Slide 5

Slide 5 text

val myFlow = flowOf("1")

Slide 6

Slide 6 text

val myFlow = flowOf("1") myFlow.collect { println(it) }

Slide 7

Slide 7 text

suspend fun run() {} CoroutineScope scope.launch { run() } Coroutines

Slide 8

Slide 8 text

Flow functions/operator types ● Builders/Creators ● Intermediate ● Terminal

Slide 9

Slide 9 text

1. Builders/Creators

Slide 10

Slide 10 text

1. Builders/Creators ● flowOf(“first”) ● emptyFlow() ● flow {} ● callbackFlow {} ● channelFlow {} ● listOf().asFlow() ● listOf>().merge()

Slide 11

Slide 11 text

channelFlow {} / callbackFlow {} callbackFlow { val listener : object : Listener{ override fun onSuccess(value : String){ sendBlocking(value) channel.close() } } locationManager.register(listener) awaitClose { locationManager.unRegister(listener) } }

Slide 12

Slide 12 text

2. Intermediate

Slide 13

Slide 13 text

2. Intermediate flowOf("1") .filter { it.isNotBlank() } .withIndex() .map { it.value } .drop(1) .take(1) .buffer(10) .debounce(10000) .distinctUntilChanged() .onStart { } .onEach { } .onCompletion { } .onEmpty { } .catch { } .retry(1) .flowOn() .transform { } .zip(flowOf("")) { s, s2 -> s } .combine(flowOf("")) { a: String , b: String -> "" } .flatMapMerge { value -> flowOf(value) } .flatMapConcat { value -> flowOf(value) } .flatMapLatest { flowOf(it) } .scan("") { accumulator: String , value: String -> accumulator + value } .runningReduce { accumulator , value -> "" }

Slide 14

Slide 14 text

Intermediates - part 1

Slide 15

Slide 15 text

● flowOf("1", "", "2").filter { it.isNotBlank() } // "1", "2" ● flowOf(1, 2).map { "$it" } // "1", "2" ● flowOf("first", "second").withIndex() // "index= 0, value= first", "index=1, value= second" ● flowOf(1, 2, 3).drop(1) // 2, 3 ● flowOf(1, 2, 3).take(1) // 1 ● flowOf(1, 1, 1, 2, 1, 3).distinctUntilChanged() // 1, 2, 1, 3

Slide 16

Slide 16 text

flowOf(1, 2, 3) .onEach { println("onEach= $it") } .buffer(10, BufferOverflow.SUSPEND) .collect { delay(1000) println("collected= $it") } // onEach= 1, onEach= 2, onEach= 3, collected= 1, collected= 2, collected= 3

Slide 17

Slide 17 text

flow { emit(1) delay(50) emit(2) delay(150) emit(3) }.debounce(100) // 2, 3

Slide 18

Slide 18 text

Intermediates - part 2

Slide 19

Slide 19 text

flowOf(1, 2, 3) .onStart { emit(4) } .onStart { emit(5) } //5, 4, 1, 2, 3

Slide 20

Slide 20 text

flowOf(1, 2, 3).onEach { println(it) delay(1000) } // 1, 2, 3 flowOf(1, 2,).filter{ it > 5 }.onEmpty { emitAll(flowOf(1, 2, 3)) } //1, 2, 3

Slide 21

Slide 21 text

flowOf(1, 2, 3).onCompletion { throwable -> if (throwable == null) { // normal cancellation emit(4) }else{ // exception occurred // we can not emit anymore } } // 1, 2, 3, 4

Slide 22

Slide 22 text

flow { throw IllegalStateException("cancel the flow") } .retryWhen { cause, attempt -> attempt < 3 } .catch { throwable -> emit(1) } // 1,

Slide 23

Slide 23 text

Intermediates - part 3

Slide 24

Slide 24 text

flowOf(1, 2, 3) .flowOn(Dispatchers.Default) .onEach { } .flowOn(Dispatchers.IO) // 1, 2, 3

Slide 25

Slide 25 text

Intermediates - part 4

Slide 26

Slide 26 text

flowOf("first", "", "second") .transform { if(it.isEmpty()) return@transform val newItems = api.search(it) emit(newItems) } fun Flow.transform(transform: suspend FlowCollector.(value: T) -> Unit ): Flow = flow { collect { value -> return@collect transform(value)}}

Slide 27

Slide 27 text

api1.get().zip(api2.get()) { f: Response1, s: Response2 -> f + s } api.fetchBookList() .combine(queryNameFlow){books: List, s: String -> books.filter { it.name.contains(s) } }

Slide 28

Slide 28 text

flatMap* ● flatMapMerge ● flatMapConcat ● flatMapLatest

Slide 29

Slide 29 text

flowOf(1, 2, 3).flatMapMerge { value -> flow { emit("a$value") delay(Random.nextLong(1000)) emit("b$value") } } // a1, a2, a3, ?

Slide 30

Slide 30 text

flowOf(1, 2, 3).flatMapConcat { value -> flow { emit("a$value") delay(Random.nextLong(1000)) emit("b$value") } } // a1, b1, a2, b2,

Slide 31

Slide 31 text

userGenerateQuery.flatMapLatest { query -> localDataBase.observeForQuery(query) } flowOf(1, 2, 3).flatMapLatest { value -> flow { delay(50) emit("a$value") delay(Random.nextLong(1000)) emit("b$value") } } // a3, b3

Slide 32

Slide 32 text

Intermediates - part 5

Slide 33

Slide 33 text

Reducers ● runningReduce ● scan

Slide 34

Slide 34 text

flowOf(1, 2, 3) .runningReduce { accumulator, value -> accumulator * value } // 1, 2, 6 resultFlow .scan(InitialState) { lastState, newResult -> lastState.createNewStateForResult(newResult) }

Slide 35

Slide 35 text

3. Terminals

Slide 36

Slide 36 text

3. Terminals ● flowOf(1, 2, 3).collect { } ● flowOf(1, 2, 3).collectIndexed { index, value -> } ● flowOf(1, 2, 3).launchIn(scope) ● flowOf(1, 2, 3).toList() ● flowOf(1, 3, 3).toSet() ● flowOf(1, 2, 3).single() ● flowOf(1, 2, 3).first() ● flowOf(1, 2, 3).reduce { acc, value -> acc + value } ● flowOf(1, 2, 3).fold("") { acc, value -> acc + value }

Slide 37

Slide 37 text

Collectors ● flowOf(1, 2, 3).collect { } // 1, 2, 3 ● flowOf(1, 2, 3).collectIndexed { index, value-> } // 0:1, 1:2, 2:3 ● flowOf(1, 2, 3).launchIn(scope)

Slide 38

Slide 38 text

Collections ● flowOf(1, 2, 3).toList() // [1, 2, 3] ● flowOf(1, 3, 3).toSet() // [1, 3]

Slide 39

Slide 39 text

Firsty ● flowOf(1, 2, 3).single() // throws IllegalStateException ● flowOf(1, 2, 3).first() // 1

Slide 40

Slide 40 text

Reducers ● flowOf(1, 2, 3).reduce { acc, value -> acc + value } //6 ● flowOf(1, 2, 3).fold("") { acc, value -> acc + value } //"123"

Slide 41

Slide 41 text

Cold vs Hot flow

Slide 42

Slide 42 text

Rowing vs Car Engine

Slide 43

Slide 43 text

Entering hot flows ● (Mutable)SharedFlow ● (Mutable)StateFlow

Slide 44

Slide 44 text

SharedFlow Builders ● MutableSharedFlow() ● socketFlow.shareIn(scope, SharingStarted.Eagerly) SharedFlow Emitters ● scope.launch{ mutableSharedFlow.emit() } ● mutableSharedFlow.tryEmit()

Slide 45

Slide 45 text

StateFlow Builders ● MutableStateFlow(0) ● flowOfStates.stateIn(scope) StateFlow Emitters ● scope.launch{ mutableSharedFlow.emit() } ● mutableSharedFlow.tryEmit() ● mutableSharedFlow.value = newValue

Slide 46

Slide 46 text

Testing @Test fun test() = runBlocking { val events = flowOf(1, 2, 3).toList() assert(events) }

Slide 47

Slide 47 text

Turbine @Test fun hotFlowTest() = runBlocking { val sharedFlow = MutableSharedFlow() sharedFlow.test { sharedFlow.emit(1) assert(expectItem()) cancelAndIgnoreRemainingEvents() }}

Slide 48

Slide 48 text

OSS projects 1. Reyan: https://github.com/islamversity/Reyan 2. FlowMarbles: https://flowmarbles.com

Slide 49

Slide 49 text

🎇 Operation successful 🎇 Happy Flowing, Kotlin Slack @worldsnas Thanks We are actively hiring in wide range of positions. make sure to checkout our openings on: https://www.revolut.com/careers