Kotlin Multiplatform - Analytics platform as example

Kotlin Multiplatform - Analytics platform as example

B11fe994ffca4aaa3da014530981ece3?s=128

Alexander Gherschon

May 21, 2019
Tweet

Transcript

  1. KOTLIN MULTIPLATFORM Analytics platform as example Alexander Gherschon

  2. AGENDA • Introduction • Configuration • Code • Demo •

    Questions
  3. INTRODUCTION

  4. GOAL Share code between all platforms

  5. WHAT PLATFORMS • Android • Server (JVM, JS, …) •

    iOS • Javascript : React (Web), Electron (Desktop), Server (Node.js), … • Windows • Linux • WebAssembly • etc.
  6. Android WHAT CODE Networking UI Domain Business Logic iOS Networking

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

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

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

    UI REACTJS UI
  10. WHAT PACKAGE Core Common Platform Specific Android Library (.aar) iOS

    (.Framework) Backend Library (.jar) Node.js Module
  11. ANALYTICS EXAMPLE analytics-core Common Platform Specific analytics-core-android.aar AnalyticsCore.Framework analytics-core-backend.jar analytics-core-node

  12. 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
  13. CONFIGURATION

  14. apply plugin: 'kotlin-multiplatform' kotlin { targets { } sourceSets {

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

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

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

    } } GRADLE PLUGIN
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. sourceSets { commonMain { dependencies {} } backendMain { dependencies

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

    {} } nodeMain { dependencies {} } iOSMain { dependencies {} } } } SOURCESETS
  26. sourceSets { commonMain { dependencies { implementation kotlin('stdlib-common') } }

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

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

    nodeMain { dependencies { implementation kotlin('stdlib-js') } } } } SOURCESETS
  29. CODE

  30. COMMON CODE (commonMain sourceset)

  31. 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 )
  32. 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())) } }
  33. 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())) } }
  34. 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())) } }
  35. 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())) } }
  36. 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())) } }
  37. 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())) } }
  38. PLATFORM SPECIFIC CODE

  39. 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() }
  40. 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() }
  41. 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() }
  42. 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() }
  43. 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() }
  44. 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() }
  45. 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() }
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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())) } }
  52. 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())) } }
  53. USING HELPER / ANDROID class HomeFragment : Fragment() { override

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

    fun onResume() { super.onResume() EventsHelper.enterScreen(Screen.HOME) } override fun onPause() { super.onPause() EventsHelper.exitScreen(Screen.HOME) } }
  55. USING HELPER / REACT class HomeScreen : RComponent<RProps, RState>() {

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

    override fun componentDidMount() { EventsHelper.enterScreen(Screen.HOME) } override fun componentWillUnmount() { EventsHelper.exitScreen(Screen.HOME) } override fun RBuilder.render() {…} }
  57. 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) } }
  58. 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) } }
  59. LIBRAIRIES

  60. KTOR + KTOR-CLIENTS

  61. KOTLINX-SERIALIZATION

  62. KOTLINX-COROUTINES

  63. DEMO

  64. NEXT STEPS

  65. ADD PERSISTENCE

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

    etc.
  67. QUESTIONS?

  68. THANK YOU!