Slide 1

Slide 1 text

KOTLIN MULTIPLATFORM Analytics platform as example Alexander Gherschon

Slide 2

Slide 2 text

AGENDA • Introduction • Configuration • Code • Demo • Questions

Slide 3

Slide 3 text

INTRODUCTION

Slide 4

Slide 4 text

GOAL Share code between all platforms

Slide 5

Slide 5 text

WHAT PLATFORMS • Android • Server (JVM, JS, …) • iOS • Javascript : React (Web), Electron (Desktop), Server (Node.js), … • Windows • Linux • WebAssembly • etc.

Slide 6

Slide 6 text

Android WHAT CODE Networking UI Domain Business Logic iOS Networking UI Domain Business Logic REACTJS Networking UI Domain Business Logic

Slide 7

Slide 7 text

Android WHAT CODE Networking UI Domain Business Logic iOS Networking UI Domain Business Logic REACTJS Networking UI Domain Business Logic

Slide 8

Slide 8 text

Android WHAT CODE Networking UI Domain Business Logic iOS Networking UI Domain Business Logic REACTJS Networking UI Domain Business Logic

Slide 9

Slide 9 text

Common Android WHAT CODE Networking UI Domain Business Logic iOS UI REACTJS UI

Slide 10

Slide 10 text

WHAT PACKAGE Core Common Platform Specific Android Library (.aar) iOS (.Framework) Backend Library (.jar) Node.js Module

Slide 11

Slide 11 text

ANALYTICS EXAMPLE analytics-core Common Platform Specific analytics-core-android.aar AnalyticsCore.Framework analytics-core-backend.jar analytics-core-node

Slide 12

Slide 12 text

ANALYTICS EXAMPLE analytics-core Common Platform Specific analytics-core-android.aar analytics-android AnalyticsCore.Framework analytics-core-backend.jar analytics-backend analytics-core-node analytics-frontend analytics-ios

Slide 13

Slide 13 text

CONFIGURATION

Slide 14

Slide 14 text

apply plugin: 'kotlin-multiplatform' kotlin { targets { } sourceSets { } } GRADLE PLUGIN

Slide 15

Slide 15 text

apply plugin: 'kotlin-multiplatform' kotlin { targets { } sourceSets { } } GRADLE PLUGIN

Slide 16

Slide 16 text

apply plugin: 'kotlin-multiplatform' kotlin { targets { } sourceSets { } } GRADLE PLUGIN

Slide 17

Slide 17 text

apply plugin: 'kotlin-multiplatform' kotlin { targets { } sourceSets { } } GRADLE PLUGIN

Slide 18

Slide 18 text

targets { android() fromPreset(presets.iosArm64, 'iOS') { binaries { framework('Analytics') } } jvm('backend') js('node') { tasks.getByName(compilations.main.compileKotlinTaskName).kotlinOptions { metaInfo = true sourceMap = true moduleKind = 'commonjs' sourceMapEmbedSources = "always" } } } TARGETS

Slide 19

Slide 19 text

targets { android() fromPreset(presets.iosArm64, 'iOS') { binaries { framework('Analytics') } } jvm('backend') js('node') { tasks.getByName(compilations.main.compileKotlinTaskName).kotlinOptions { metaInfo = true sourceMap = true moduleKind = 'commonjs' sourceMapEmbedSources = "always" } } } TARGETS

Slide 20

Slide 20 text

targets { android() fromPreset(presets.iosArm64, 'iOS') { binaries { framework('Analytics') } } jvm('backend') js('node') { tasks.getByName(compilations.main.compileKotlinTaskName).kotlinOptions { metaInfo = true sourceMap = true moduleKind = 'commonjs' sourceMapEmbedSources = "always" } } } TARGETS

Slide 21

Slide 21 text

targets { android() fromPreset(presets.iosArm64, 'iOS') { binaries { framework('Analytics') } } jvm('backend') js('node') { tasks.getByName(compilations.main.compileKotlinTaskName).kotlinOptions { metaInfo = true sourceMap = true moduleKind = 'commonjs' sourceMapEmbedSources = "always" } } } TARGETS

Slide 22

Slide 22 text

targets { android() fromPreset(presets.iosArm64, 'iOS') { binaries { framework('Analytics') } } jvm('backend') js('node') { tasks.getByName(compilations.main.compileKotlinTaskName).kotlinOptions { metaInfo = true sourceMap = true moduleKind = 'commonjs' sourceMapEmbedSources = "always" } } } TARGETS

Slide 23

Slide 23 text

targets { android() fromPreset(presets.iosArm64, 'iOS') { binaries { framework('Analytics') } } jvm('backend') js('node') { tasks.getByName(compilations.main.compileKotlinTaskName).kotlinOptions { metaInfo = true sourceMap = true moduleKind = 'commonjs' sourceMapEmbedSources = "always" } } } TARGETS

Slide 24

Slide 24 text

sourceSets { commonMain { dependencies {} } backendMain { dependencies {} } nodeMain { dependencies {} } iOSMain { dependencies {} } } } SOURCESETS

Slide 25

Slide 25 text

sourceSets { commonMain { dependencies {} } backendMain { dependencies {} } nodeMain { dependencies {} } iOSMain { dependencies {} } } } SOURCESETS

Slide 26

Slide 26 text

sourceSets { commonMain { dependencies { implementation kotlin('stdlib-common') } } nodeMain { dependencies { implementation kotlin('stdlib-js') } } } } SOURCESETS

Slide 27

Slide 27 text

sourceSets { commonMain { dependencies { implementation kotlin('stdlib-common') } } nodeMain { dependencies { implementation kotlin('stdlib-js') } } } } SOURCESETS

Slide 28

Slide 28 text

sourceSets { commonMain { dependencies { implementation kotlin('stdlib-common') } } nodeMain { dependencies { implementation kotlin('stdlib-js') } } } } SOURCESETS

Slide 29

Slide 29 text

CODE

Slide 30

Slide 30 text

COMMON CODE (commonMain sourceset)

Slide 31

Slide 31 text

MODELS enum class ScreenAction { ENTER, EXIT } enum class Platform { ANDROID, IOS, REACT } enum class Screen { HOME, DASHBOARD } data class Event( val name: String, val screen: Screen, val timestamp: Long, val platform: Platform )

Slide 32

Slide 32 text

HELPER object EventsHelper { private fun logEvent(event: Event) { println("logging the event = $event") } fun enterScreen(screen: Screen) { logEvent(Event(name = ScreenAction.ENTER.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform()) ) } fun exitScreen(screen: Screen) { logEvent(Event(name = ScreenAction.EXIT.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform())) } }

Slide 33

Slide 33 text

HELPER object EventsHelper { private fun logEvent(event: Event) { println("logging the event = $event") } fun enterScreen(screen: Screen) { logEvent(Event(name = ScreenAction.ENTER.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform()) ) } fun exitScreen(screen: Screen) { logEvent(Event(name = ScreenAction.EXIT.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform())) } }

Slide 34

Slide 34 text

HELPER object EventsHelper { private fun logEvent(event: Event) { println("logging the event = $event") } fun enterScreen(screen: Screen) { logEvent(Event(name = ScreenAction.ENTER.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform()) ) } fun exitScreen(screen: Screen) { logEvent(Event(name = ScreenAction.EXIT.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform())) } }

Slide 35

Slide 35 text

HELPER object EventsHelper { private fun logEvent(event: Event) { println("logging the event = $event") } fun enterScreen(screen: Screen) { logEvent(Event(name = ScreenAction.ENTER.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform()) ) } fun exitScreen(screen: Screen) { logEvent(Event(name = ScreenAction.EXIT.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform())) } }

Slide 36

Slide 36 text

HELPER object EventsHelper { private fun logEvent(event: Event) { println("logging the event = $event") } fun enterScreen(screen: Screen) { logEvent(Event(name = ScreenAction.ENTER.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform()) ) } fun exitScreen(screen: Screen) { logEvent(Event(name = ScreenAction.EXIT.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform())) } }

Slide 37

Slide 37 text

HELPER object EventsHelper { private fun logEvent(event: Event) { println("logging the event = $event") } fun enterScreen(screen: Screen) { logEvent(Event(name = ScreenAction.ENTER.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform()) ) } fun exitScreen(screen: Screen) { logEvent(Event(name = ScreenAction.EXIT.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform())) } }

Slide 38

Slide 38 text

PLATFORM SPECIFIC CODE

Slide 39

Slide 39 text

GOT THE TIME? // Common Sourceset expect fun getCurrentTimeMillis(): Long // Android/JVM Sourceset actual fun getCurrentTimeMillis(): Long { return System.currentTimeMillis() } // iOS Sourceset actual fun getCurrentTimeMillis(): Long { return NSDate().timeIntervalSince1970().toLong() } // Javascript Sourceset actual fun getCurrentTimeMillis(): Long { return Date().getTime().toLong() }

Slide 40

Slide 40 text

GOT THE TIME? // Common Sourceset expect fun getCurrentTimeMillis(): Long // Android/JVM Sourceset actual fun getCurrentTimeMillis(): Long { return System.currentTimeMillis() } // iOS Sourceset actual fun getCurrentTimeMillis(): Long { return NSDate().timeIntervalSince1970().toLong() } // Javascript Sourceset actual fun getCurrentTimeMillis(): Long { return Date().getTime().toLong() }

Slide 41

Slide 41 text

GOT THE TIME? // Common Sourceset expect fun getCurrentTimeMillis(): Long // Android/JVM Sourceset actual fun getCurrentTimeMillis(): Long { return System.currentTimeMillis() } // iOS Sourceset actual fun getCurrentTimeMillis(): Long { return NSDate().timeIntervalSince1970().toLong() } // Javascript Sourceset actual fun getCurrentTimeMillis(): Long { return Date().getTime().toLong() }

Slide 42

Slide 42 text

GOT THE TIME? // Common Sourceset expect fun getCurrentTimeMillis(): Long // Android/JVM Sourceset actual fun getCurrentTimeMillis(): Long { return System.currentTimeMillis() } // iOS Sourceset actual fun getCurrentTimeMillis(): Long { return NSDate().timeIntervalSince1970().toLong() } // Javascript Sourceset actual fun getCurrentTimeMillis(): Long { return Date().getTime().toLong() }

Slide 43

Slide 43 text

GOT THE TIME? // Common Sourceset expect fun getCurrentTimeMillis(): Long // Android/JVM Sourceset actual fun getCurrentTimeMillis(): Long { return System.currentTimeMillis() } // iOS Sourceset actual fun getCurrentTimeMillis(): Long { return NSDate().timeIntervalSince1970().toLong() } // Javascript Sourceset actual fun getCurrentTimeMillis(): Long { return Date().getTime().toLong() }

Slide 44

Slide 44 text

GOT THE TIME? // Common Sourceset expect fun getCurrentTimeMillis(): Long // Android/JVM Sourceset actual fun getCurrentTimeMillis(): Long { return System.currentTimeMillis() } // iOS Sourceset actual fun getCurrentTimeMillis(): Long { return NSDate().timeIntervalSince1970().toLong() } // Javascript Sourceset actual fun getCurrentTimeMillis(): Long { return Date().getTime().toLong() }

Slide 45

Slide 45 text

GOT THE TIME? // Common Sourceset expect fun getCurrentTimeMillis(): Long // Android/JVM Sourceset actual fun getCurrentTimeMillis(): Long { return System.currentTimeMillis() } // iOS Sourceset actual fun getCurrentTimeMillis(): Long { return NSDate().timeIntervalSince1970().toLong() } // Javascript Sourceset actual fun getCurrentTimeMillis(): Long { return Date().getTime().toLong() }

Slide 46

Slide 46 text

GOT THE PLATFORM? // Common Sourceset expect fun getPlatform(): Platform // Android/JVM Sourceset actual fun getPlatform(): Platform = Platform.ANDROID // iOS Sourceset actual fun getPlatform(): Platform = Platform.IOS // Javascript Sourceset actual fun getPlatform(): Platform = Platform.REACT

Slide 47

Slide 47 text

GOT THE PLATFORM? // Common Sourceset expect fun getPlatform(): Platform // Android/JVM Sourceset actual fun getPlatform(): Platform = Platform.ANDROID // iOS Sourceset actual fun getPlatform(): Platform = Platform.IOS // Javascript Sourceset actual fun getPlatform(): Platform = Platform.REACT

Slide 48

Slide 48 text

GOT THE PLATFORM? // Common Sourceset expect fun getPlatform(): Platform // Android/JVM Sourceset actual fun getPlatform(): Platform = Platform.ANDROID // iOS Sourceset actual fun getPlatform(): Platform = Platform.IOS // Javascript Sourceset actual fun getPlatform(): Platform = Platform.REACT

Slide 49

Slide 49 text

GOT THE PLATFORM? // Common Sourceset expect fun getPlatform(): Platform // Android/JVM Sourceset actual fun getPlatform(): Platform = Platform.ANDROID // iOS Sourceset actual fun getPlatform(): Platform = Platform.IOS // Javascript Sourceset actual fun getPlatform(): Platform = Platform.REACT

Slide 50

Slide 50 text

GOT THE PLATFORM? // Common Sourceset expect fun getPlatform(): Platform // Android/JVM Sourceset actual fun getPlatform(): Platform = Platform.ANDROID // iOS Sourceset actual fun getPlatform(): Platform = Platform.IOS // Javascript Sourceset actual fun getPlatform(): Platform = Platform.REACT

Slide 51

Slide 51 text

HELPER object EventsHelper { private fun logEvent(event: Event) { println("logging the event = $event") } fun enterScreen(screen: Screen) { logEvent(Event(name = ScreenAction.ENTER.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform()) ) } fun exitScreen(screen: Screen) { logEvent(Event(name = ScreenAction.EXIT.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform())) } }

Slide 52

Slide 52 text

HELPER object EventsHelper { private fun logEvent(event: Event) { println("logging the event = $event") } fun enterScreen(screen: Screen) { logEvent(Event(name = ScreenAction.ENTER.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform()) ) } fun exitScreen(screen: Screen) { logEvent(Event(name = ScreenAction.EXIT.name, screen = screen, timestamp = getCurrentTimeMillis(), platform = getPlatform())) } }

Slide 53

Slide 53 text

USING HELPER / ANDROID class HomeFragment : Fragment() { override fun onResume() { super.onResume() EventsHelper.enterScreen(Screen.HOME) } override fun onPause() { super.onPause() EventsHelper.exitScreen(Screen.HOME) } }

Slide 54

Slide 54 text

USING HELPER / ANDROID class HomeFragment : Fragment() { override fun onResume() { super.onResume() EventsHelper.enterScreen(Screen.HOME) } override fun onPause() { super.onPause() EventsHelper.exitScreen(Screen.HOME) } }

Slide 55

Slide 55 text

USING HELPER / REACT class HomeScreen : RComponent() { override fun componentDidMount() { EventsHelper.enterScreen(Screen.HOME) } override fun componentWillUnmount() { EventsHelper.exitScreen(Screen.HOME) } override fun RBuilder.render() {…} }

Slide 56

Slide 56 text

USING HELPER / REACT class HomeScreen : RComponent() { override fun componentDidMount() { EventsHelper.enterScreen(Screen.HOME) } override fun componentWillUnmount() { EventsHelper.exitScreen(Screen.HOME) } override fun RBuilder.render() {…} }

Slide 57

Slide 57 text

USING HELPER / IOS import UIKit import Analytics class HomeController: UIViewController { override func viewDidAppear(_ animated: Bool) { EventsHelper().enterScreen(screen: Screen.home) } override func viewDidDisappear(_ animated: Bool) { EventsHelper().exitScreen(screen: Screen.home) } }

Slide 58

Slide 58 text

USING HELPER / IOS import UIKit import Analytics class HomeController: UIViewController { override func viewDidAppear(_ animated: Bool) { EventsHelper().enterScreen(screen: Screen.home) } override func viewDidDisappear(_ animated: Bool) { EventsHelper().exitScreen(screen: Screen.home) } }

Slide 59

Slide 59 text

LIBRAIRIES

Slide 60

Slide 60 text

KTOR + KTOR-CLIENTS

Slide 61

Slide 61 text

KOTLINX-SERIALIZATION

Slide 62

Slide 62 text

KOTLINX-COROUTINES

Slide 63

Slide 63 text

DEMO

Slide 64

Slide 64 text

NEXT STEPS

Slide 65

Slide 65 text

ADD PERSISTENCE

Slide 66

Slide 66 text

ADD REPORTERS • Firebase Analytics • Mixpanel • AppsFlyer • etc.

Slide 67

Slide 67 text

QUESTIONS?

Slide 68

Slide 68 text

THANK YOU!