Slide 1

Slide 1 text

Fast Prototypes using Kotlin/Native (and Flutter) Ayelen Chavez, JB Lorenzo Jan 17, 2019

Slide 2

Slide 2 text

Agenda ● Who are we ● Our story: what problem do I want to solve ● Tools we used ● The code! ● Recap ● Q&A

Slide 3

Slide 3 text

Who we are...

Slide 4

Slide 4 text

We fuel local economies by making it super easy for anyone to buy or sell almost anything through our platforms

Slide 5

Slide 5 text

We operate a network of market-leading trading platforms in over 40 countries that are used by more than 350 million people every month

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Mana ger Mobi Mobi Mobi Ayelen Chavez Rem Cruz Cristiano Madeira JB Lorenzo

Slide 8

Slide 8 text

Once upon a time...

Slide 9

Slide 9 text

Once upon a time… This is a story about how we made the OLX Group Product & Tech Conference app in record time.

Slide 10

Slide 10 text

● We were only 4 engineers (or 3 and a half) ● We had our ambitious priorities for the quarter ● We didn't have much spare time ● The VP of Engineer asked us to help with a new app

Slide 11

Slide 11 text

● We were only 4 engineers (or 3 and a half) ● We had our ambitious priorities for the quarter ● We didn't have much spare time ● The VP of Engineer asked us to help with a new app

Slide 12

Slide 12 text

● We were only 4 engineers (or 3 and a half) ● We had our ambitious priorities for the quarter ● We didn't have much spare time ● The VP of Engineer asked us to help with a new app

Slide 13

Slide 13 text

● We were only 4 engineers (or 3 and a half) ● We had our ambitious priorities for the quarter ● We didn't have much spare time ● The VP of Engineer asked us to help with a new app

Slide 14

Slide 14 text

Spoiler Alert! We built an app in 10 days

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Before we start...

Slide 17

Slide 17 text

We needed a backend

Slide 18

Slide 18 text

We needed a backend

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

We needed a UI/UX

Slide 23

Slide 23 text

We needed a UI/UX Super hero Karen Banzon

Slide 24

Slide 24 text

We needed a UI/UX Super hero Karen Banzon Material Theme Editor in Sketch

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Shared UI and Model

Slide 27

Slide 27 text

Flutter & Kotlin/Native Shared UI/UX

Slide 28

Slide 28 text

Flutter & Kotlin/Native Shared UI/UX Shared Model

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

// GetSchedulesUseCase.kt class GetSchedulesUseCase(val repository: ConferenceRepository): UseCase { private val noFavoriteKeywords = listOf("coffee break", "lunch") override var disposeBag: Disposal = emptyArray().toMutableList() override fun buildUseCaseObservable(params: Input): Observable { return create { o -> FetchConferenceUseCase(repository).execute(Observer(onNext = { var schedules = emptyList>().toMutableList() it.talks.forEach { schedules.add(mapOf(...)) } o.onNext(schedules) }, onError = { o.onError(it) }), Unit)

Slide 33

Slide 33 text

// GetSchedulesUseCase.kt class GetSchedulesUseCase(val repository: ConferenceRepository): UseCase { private val noFavoriteKeywords = listOf("coffee break", "lunch") override var disposeBag: Disposal = emptyArray().toMutableList() override fun buildUseCaseObservable(params: Input): Observable { return create { o -> FetchConferenceUseCase(repository).execute(Observer(onNext = { var schedules = emptyList>().toMutableList() it.talks.forEach { schedules.add(mapOf(...)) } o.onNext(schedules) }, onError = { o.onError(it) }), Unit)

Slide 34

Slide 34 text

// GetSchedulesUseCase.kt class GetSchedulesUseCase(val repository: ConferenceRepository): UseCase { private val noFavoriteKeywords = listOf("coffee break", "lunch") override var disposeBag: Disposal = emptyArray().toMutableList() override fun buildUseCaseObservable(params: Input): Observable { return create { o -> FetchConferenceUseCase(repository).execute(Observer(onNext = { var schedules = emptyList>().toMutableList() it.talks.forEach { schedules.add(mapOf(...)) } o.onNext(schedules) }, onError = { o.onError(it) }), Unit)

Slide 35

Slide 35 text

// GetSchedulesUseCase.kt class GetSchedulesUseCase(val repository: ConferenceRepository): UseCase { private val noFavoriteKeywords = listOf("coffee break", "lunch") override var disposeBag: Disposal = emptyArray().toMutableList() override fun buildUseCaseObservable(params: Input): Observable { return create { o -> FetchConferenceUseCase(repository).execute(Observer(onNext = { var schedules = emptyList>().toMutableList() it.talks.forEach { schedules.add(mapOf(...)) } o.onNext(schedules) }, onError = { o.onError(it) }), Unit)

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

// MainActivity.kt import com.olxgroup.olxconf.domain.GetSchedulesUseCase val schedulesUseCase = GetSchedulesUseCase(repository) MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "getSchedules" -> getSchedules(result) "getSpeakers" -> getSpeakers(result) "getInfo" -> getInfo(result) "launchMap" -> launchMap(result, call) } }

Slide 39

Slide 39 text

// MainActivity.kt import com.olxgroup.olxconf.domain.GetSchedulesUseCase val schedulesUseCase = GetSchedulesUseCase(repository) MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "getSchedules" -> getSchedules(result) "getSpeakers" -> getSpeakers(result) "getInfo" -> getInfo(result) "launchMap" -> launchMap(result, call) } }

Slide 40

Slide 40 text

// MainActivity.kt import com.olxgroup.olxconf.domain.GetSchedulesUseCase val schedulesUseCase = GetSchedulesUseCase(repository) MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "getSchedules" -> getSchedules(result) "getSpeakers" -> getSpeakers(result) "getInfo" -> getInfo(result) "launchMap" -> launchMap(result, call) } }

Slide 41

Slide 41 text

// MainActivity.kt import com.olxgroup.olxconf.domain.GetSchedulesUseCase val schedulesUseCase = GetSchedulesUseCase(repository) MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "getSchedules" -> getSchedules(result) "getSpeakers" -> getSpeakers(result) "getInfo" -> getInfo(result) "launchMap" -> launchMap(result, call) } }

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

// AppDelegate.swift import OLXConfFramework let channel = FlutterMethodChannel.init(name: "/api", binaryMessenger: controller) channel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in if call.method == "getSchedules" { self.getSchedules(result) } else if call.method == "getSpeakers" { self.getSpeakers(result) } else if call.method == "getInfo" { self.getInfo(result) } else if call.method == "launchMap" { self.launchMap(call, result) } });

Slide 45

Slide 45 text

// AppDelegate.swift import OLXConfFramework let channel = FlutterMethodChannel.init(name: "/api", binaryMessenger: controller) channel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in if call.method == "getSchedules" { self.getSchedules(result) } else if call.method == "getSpeakers" { self.getSpeakers(result) } else if call.method == "getInfo" { self.getInfo(result) } else if call.method == "launchMap" { self.launchMap(call, result) } });

Slide 46

Slide 46 text

// AppDelegate.swift import OLXConfFramework let channel = FlutterMethodChannel.init(name: "/api", binaryMessenger: controller) channel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in if call.method == "getSchedules" { self.getSchedules(result) } else if call.method == "getSpeakers" { self.getSpeakers(result) } else if call.method == "getInfo" { self.getInfo(result) } else if call.method == "launchMap" { self.launchMap(call, result) } });

Slide 47

Slide 47 text

// AppDelegate.swift import OLXConfFramework let channel = FlutterMethodChannel.init(name: "/api", binaryMessenger: controller) channel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in if call.method == "getSchedules" { self.getSchedules(result) } else if call.method == "getSpeakers" { self.getSpeakers(result) } else if call.method == "getInfo" { self.getInfo(result) } else if call.method == "launchMap" { self.launchMap(call, result) } });

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

// data_schedule.dart static const platform = const MethodChannel('/api'); final List result = await platform.invokeMethod('getSchedules');

Slide 51

Slide 51 text

// data_schedule.dart static const platform = const MethodChannel('/api'); final List result = await platform.invokeMethod('getSchedules');

Slide 52

Slide 52 text

// data_schedule.dart static const platform = const MethodChannel('/api'); final List result = await platform.invokeMethod('getSchedules');

Slide 53

Slide 53 text

Connecting Flutter with the external world

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

Common Model

Slide 56

Slide 56 text

Common Model Android Module iOS Module

Slide 57

Slide 57 text

Common Model Android Module iOS Module Common UI/UX

Slide 58

Slide 58 text

Recap...

Slide 59

Slide 59 text

Pitfalls

Slide 60

Slide 60 text

● Packing and Unpacking ● Companion in Swift ● arm32 ● JVM libraries

Slide 61

Slide 61 text

● Packing and Unpacking ● Companion in Swift ● arm32 ● JVM libraries

Slide 62

Slide 62 text

● Packing and Unpacking ● Companion in Swift ● arm32 ● JVM libraries

Slide 63

Slide 63 text

● Packing and Unpacking ● Companion in Swift ● arm32 ● JVM libraries

Slide 64

Slide 64 text

Takeaways

Slide 65

Slide 65 text

● Flutter is awesome for fast prototypes ● Flutter and Kotlin/Native are still in beta but is usable already though with some limitations ● Kotlin/Native works well to reduce the amount of platform specific code ● Some glue code needs to be written to pass around/serialize objects ● Don’t be afraid to try out new technologies (but …)

Slide 66

Slide 66 text

● Flutter is awesome for fast prototypes ● Flutter and Kotlin/Native are still in beta but is usable already though with some limitations ● Kotlin/Native works well to reduce the amount of platform specific code ● Some glue code needs to be written to pass around/serialize objects ● Don’t be afraid to try out new technologies (but …)

Slide 67

Slide 67 text

● Flutter is awesome for fast prototypes ● Flutter and Kotlin/Native are still in beta but is usable already though with some limitations ● Kotlin/Native works well to reduce the amount of platform specific code ● Some glue code needs to be written to pass around/serialize objects ● Don’t be afraid to try out new technologies (but …)

Slide 68

Slide 68 text

● Flutter is awesome for fast prototypes ● Flutter and Kotlin/Native are still in beta but is usable already though with some limitations ● Kotlin/Native works well to reduce the amount of platform specific code ● Some glue code needs to be written to pass around/serialize objects ● Don’t be afraid to try out new technologies (but …)

Slide 69

Slide 69 text

● Flutter is awesome for fast prototypes ● Flutter and Kotlin/Native are still in beta but is usable already though with some limitations ● Kotlin/Native works well to reduce the amount of platform specific code ● Some glue code needs to be written to pass around/serialize objects ● Don’t be afraid to try out new technologies (but …)

Slide 70

Slide 70 text

Questions Email: [email protected] and [email protected] We are hiring: joinolx.com Medium post: http://bit.ly/kotlin_flutter