Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin Multiplatform - Analytics platform as example

Kotlin Multiplatform - Analytics platform as example

Alexander Gherschon

May 21, 2019
Tweet

More Decks by Alexander Gherschon

Other Decks in Programming

Transcript

  1. KOTLIN MULTIPLATFORM
    Analytics platform as example
    Alexander Gherschon

    View full-size slide

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

    View full-size slide

  3. INTRODUCTION

    View full-size slide

  4. GOAL
    Share code between all platforms

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  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

    View full-size slide

  13. CONFIGURATION

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  29. COMMON CODE
    (commonMain sourceset)

    View full-size slide

  30. 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
    )

    View full-size slide

  31. 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()))
    }
    }

    View full-size slide

  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()))
    }
    }

    View full-size slide

  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()))
    }
    }

    View full-size slide

  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()))
    }
    }

    View full-size slide

  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()))
    }
    }

    View full-size slide

  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()))
    }
    }

    View full-size slide

  37. PLATFORM SPECIFIC CODE

    View full-size slide

  38. 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()
    }

    View full-size slide

  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()
    }

    View full-size slide

  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()
    }

    View full-size slide

  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()
    }

    View full-size slide

  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()
    }

    View full-size slide

  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()
    }

    View full-size slide

  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()
    }

    View full-size slide

  45. 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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  50. 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()))
    }
    }

    View full-size slide

  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()))
    }
    }

    View full-size slide

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

    View full-size slide

  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)
    }
    }

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  56. 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)
    }
    }

    View full-size slide

  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)
    }
    }

    View full-size slide

  58. KTOR + KTOR-CLIENTS

    View full-size slide

  59. KOTLINX-SERIALIZATION

    View full-size slide

  60. KOTLINX-COROUTINES

    View full-size slide

  61. ADD PERSISTENCE

    View full-size slide

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

    View full-size slide