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

Kotlin Multiplatform in Production

Kotlin Multiplatform in Production

Update on the state of the Kotlin Multiplatform universe, current libraries, and how to get started.

Kevin Galligan

November 19, 2018
Tweet

More Decks by Kevin Galligan

Other Decks in Programming

Transcript

  1. Kotlin Multiplatform In Production
    Kevin Galligan

    View Slide

  2. View Slide

  3. Touchlab

    View Slide

  4. Community
    community!

    View Slide

  5. View Slide

  6. What is Kotlin Multiplatform?

    View Slide

  7. kot·lin mul·ti·plat·form
    /ˌkätˈlin məltiˈplatfôrm,ˌkätˈlin məltīˈplatfôrm/
    noun
    noun: kotlin multiplatform
    1.optional, natively-integrated, open-source, code sharing platform, based on the popular,
    modern language kotlin. facilitates non-ui logic availability on many platforms.

    View Slide

  8. Optional Sharing
    Low risk. No Big Decisions.

    View Slide

  9. Natively Integrated
    smooth interop

    View Slide

  10. open source

    View Slide

  11. Code Sharing
    not “cross platform”

    View Slide

  12. popular

    View Slide

  13. popular

    View Slide

  14. popular

    View Slide

  15. language & tools

    View Slide

  16. modern

    View Slide

  17. Not UI
    well, not necessarily UI

    View Slide

  18. Shared UI == Failure!

    View Slide

  19. Shared Logic == Computers

    View Slide

  20. Many Platforms

    View Slide

  21. Kotlin

    View Slide

  22. Kotlin
    JVM

    View Slide

  23. Kotlin
    JVM JS

    View Slide

  24. Kotlin
    JVM JS Native

    View Slide

  25. Kotlin
    JVM JS Native
    iOS Mac
    Linux Windows
    Android/NDK Others…

    View Slide

  26. Kotlin
    JVM JS Native
    iOS Mac
    Linux Windows
    Android/NDK Others…
    Webassembly

    View Slide

  27. Kotlin
    JVM JS Native
    iOS Mac
    Linux Windows
    Android/NDK Others…
    Webassembly
    Many Platforms

    View Slide

  28. Why Kotlin?
    High Efficiency
    Low Risk
    Modern Language/Tools
    Highly Engaged Community

    View Slide

  29. mobile is easy

    View Slide

  30. mobile is easy

    View Slide

  31. Status

    View Slide

  32. Q3
    Q2 Q4 Q1 Q2
    2018 2019
    0 .6
    v0.7
    v0.8
    v0.8.2
    v0.9.3
    IDE tooling!
    Coroutines?

    View Slide

  33. Q3
    Q2 Q4 Q1 Q2
    2018 2019
    0 .6
    v0.7
    v0.8
    v0.8.2
    v0.9.3
    IDE tooling!
    Coroutines? K/N 1.0, Kotlin 1.3
    Gradle 4.10+
    Android Studio

    View Slide

  34. Q3
    Q2 Q4 Q1 Q2
    2018 2019
    0 .6
    v0.7
    v0.8
    v0.8.2
    v0.9.3
    IDE tooling!
    Coroutines? K/N 1.0, Kotlin 1.3
    Gradle 4.10+
    Android Studio
    Other samples/libraries
    Production deployments
    Compiler plugins!
    MT Coroutines!

    View Slide

  35. Q3
    Q2 Q4 Q1 Q2
    2018 2019
    0 .6
    v0.7
    v0.8
    v0.8.2
    v0.9.3
    IDE tooling!
    Coroutines? K/N 1.0, Kotlin 1.3
    Gradle 4.10+
    Android Studio
    Compiler plugins!
    Other samples/libraries
    Production deployments
    Paid license/debugger
    Reactive Library
    Big Production Apps
    Webassembly stuff?
    MT Coroutines!

    View Slide

  36. TL;DR March
    start getting real end of Q1

    View Slide

  37. SHARED CODE
    FOR
    ANDROID & IOS

    View Slide

  38. Common

    View Slide

  39. Common mainThread?

    View Slide

  40. expect val mainThread:Boolean

    View Slide

  41. expect val mainThread:Boolean
    actual val mainThread: Boolean
    get() = Looper.myLooper() === Looper.getMainLooper()

    View Slide

  42. expect val mainThread:Boolean
    actual val mainThread: Boolean
    get() = Looper.myLooper() === Looper.getMainLooper()
    actual val mainThread: Boolean
    get() = NSThread.isMainThread()

    View Slide

  43. expect val mainThread:Boolean
    actual val mainThread: Boolean
    get() = Looper.myLooper() === Looper.getMainLooper()
    actual val mainThread: Boolean
    get() = NSThread.isMainThread()
    actual val mainThread: Boolean = true

    View Slide

  44. expect fun currentTimeMillis():Long
    expect fun backgroundTask(backJob:()-> B, mainJob:(B) -> Unit)
    expect fun backgroundTask(backJob:()->Unit)
    expect fun networkBackgroundTask(backJob:()->Unit)
    expect fun initContext():NativeOpenHelperFactory
    expect fun goFreeze(a:T):T
    expect fun T.freeze2(): T
    expect fun simpleGet(url:String):String
    expect fun logException(t:Throwable)
    expect fun settingsFactory(): Settings.Factory
    expect fun createUuid():String

    View Slide

  45. expect class Date {
    fun toLongMillis():Long
    }
    expect class DateFormatHelper(format:String){
    fun toDate(s:String):Date
    fun format(d:Date):String
    }

    View Slide

  46. actual class Date(val date:java.util.Date) {
    actual fun toLongMillis(): Long = date.time
    }
    actual class DateFormatHelper actual constructor(format: String) {
    val dateFormatter = object : ThreadLocal(){
    override fun initialValue(): DateFormat = SimpleDateFormat(format)
    }
    actual fun toDate(s: String): Date = Date(dateFormatter.get()!!.parse(s))
    actual fun format(d: Date): String = dateFormatter.get()!!.format(d.date)
    }

    View Slide

  47. fun initPlatformClient(
    staticFileLoader: (filePrefix: String, fileType: String) -> String?,
    analyticsCallback: (name: String, params: Map) -> Unit,
    clLogCallback: (s: String) -> Unit) {

    View Slide

  48. fun initPlatformClient(
    staticFileLoader: (filePrefix: String, fileType: String) -> String?,
    analyticsCallback: (name: String, params: Map) -> Unit,
    clLogCallback: (s: String) -> Unit) {
    AppContext.initPlatformClient ({filePrefix, fileType ->
    loadAsset("${filePrefix}.${fileType}")},
    {name: String, params: Map ->
    val event = CustomEvent(name)
    //Loop
    Answers.getInstance().logCustom(event)
    },
    { Log.w("MainApp", it) })

    View Slide

  49. let appContext = AppContext()
    appContext.doInitPlatformClient(staticFileLoader: loadAsset,
    analyticsCallback:
    analyticsCallback,
    clLogCallback: csLog)
    func loadAsset(filePrefix:String, fileType:String) -> String?{
    do{
    let bundleFile = Bundle.main.path(forResource: filePrefix,
    ofType: fileType)
    return try String(contentsOfFile: bundleFile!)
    } catch {
    return nil
    }
    }

    View Slide

  50. Common

    View Slide

  51. JVM Native
    Common

    View Slide

  52. JVM Native
    Common
    Framework

    View Slide

  53. JVM Native
    Common
    Framework

    View Slide

  54. JVM Native
    Common
    Android
    Stuff
    Framework
    iOS Stuff

    View Slide

  55. Libraries!

    View Slide

  56. Jetbrains
    • Ktor
    • Kotlinx.Coroutines
    • Kotlinx.io
    • Kotlinx.serialization
    • Atomic-fu (maybe?)

    View Slide

  57. Community
    • Sqldelight
    • Knarch.db
    • Multiplatform Settings
    • Stately
    • OKIO2 (developing)
    • Timber (sort of)

    View Slide

  58. Community
    • Sqldelight
    • Knarch.db
    • Multiplatform Settings
    • Stately
    • OKIO2 (developing)
    • Timber (sort of)

    View Slide

  59. View Slide

  60. SQLiter
    trim, lightly opinionated SQLite driver

    View Slide

  61. SQLit
    trim, lightly opinionated SQLite driver

    View Slide

  62. Targets
    Native SQLite

    View Slide

  63. Targets
    Native SQLite
    Native SQL
    Encrypted

    View Slide

  64. Targets
    Native SQLite
    Native SQL
    Encrypted
    Android SQLite

    View Slide

  65. Targets
    Native SQLite
    Native SQL
    Encrypted
    Android SQLite
    Android SQL
    Encrypted

    View Slide

  66. Targets
    Native SQLite
    Native SQL
    Encrypted
    Android SQLite
    Android SQL
    Encrypted
    JS?

    View Slide

  67. View Slide

  68. View Slide

  69. Public Service Announcement
    if you understand anything about native…

    View Slide

  70. STATE

    View Slide

  71. 3 Ecosystems
    JVM, JS, and Native

    View Slide

  72. Kotlin/Native State Rules

    View Slide

  73. Rule #1
    Live state belongs to 1 thread

    View Slide

  74. Rule #2
    Frozen state can be shared by threads

    View Slide

  75. No threading primitives
    No “synchronized”, “volatile”, etc

    View Slide

  76. Runtime Safety
    Kotlin/Native can verify safe mutability

    View Slide

  77. JVM/JS?
    See Kotlinconf keynote

    View Slide

  78. Short term pain
    Tradeoff for future

    View Slide

  79. How does Kotlin know?!

    View Slide

  80. FROZEN!

    View Slide

  81. FROZEN!

    View Slide

  82. Runtime Designation
    AKA a flag

    View Slide

  83. Call freeze()

    View Slide

  84. One-way operation
    No unfreeze()

    View Slide

  85. class TalkExamples{
    var justCountingStuff:Int = 0
    init {
    backgroundCall {
    //do something
    justCountingStuff++
    }.freeze()
    }
    }

    View Slide

  86. class TalkExamples{
    var justCountingStuff:Int = 0
    init {
    backgroundCall {
    //do something
    justCountingStuff++
    }.freeze()
    }
    }

    View Slide

  87. Usually OK
    Data objects should be immutable

    View Slide

  88. Global state more difficult
    Service object, large memory state

    View Slide

  89. Passing State

    View Slide

  90. DetachedObjectGraph(TransferMode.SAFE) { ListData("asdf") }

    View Slide

  91. val data = ListData("asdf")
    DetachedObjectGraph(TransferMode.SAFE) { data }

    View Slide

  92. private val stateBox: AtomicReference> = AtomicReference(
    DetachedObjectGraph(mode = TransferMode.SAFE,
    producer = { mutableListOf() as Any })
    )
    private val lock = NSLock()
    internal fun withLockDetached(proc: (MutableList) -> MutableList) {
    lock.lock()
    try {
    stateBox.value = DetachedObjectGraph(mode = TransferMode.SAFE,
    producer = {
    val dataList = stateBox.value.attach() as MutableList
    proc(dataList) as Any
    })
    } finally {
    lock.unlock()
    }
    }

    View Slide

  93. Detaching Time
    must visit whole graph

    View Slide

  94. View Slide

  95. View Slide

  96. View Slide

  97. Atomics!
    Mutable immutable

    View Slide

  98. AtomicInt/AtomicLong

    View Slide

  99. AtomicReference
    Update with frozen objects

    View Slide

  100. val lambdas = AtomicReference(null)
    fun initPlatformClient(
    staticFileLoader: (filePrefix: String, fileType: String) -> String?,
    analyticsCallback: (name: String, params: Map) -> Unit,
    clLogCallback: (s: String) -> Unit) {
    lambdas.value = PlatformLambdas(
    staticFileLoader,
    analyticsCallback,
    clLogCallback).freeze()
    }

    View Slide

  101. Stately!
    v0.3.1~ish

    View Slide

  102. Multiplatform Definitions
    • freeze() method and frozen info
    • Atomics (Int, Long, Reference)
    • K/N state-related annotations (@ThreadLocal/
    @SharedImmutable)
    • Shared Collections!

    View Slide

  103. More Info and Tutorials
    https://github.com/touchlab/KotlinMultiplatformStuff

    View Slide

  104. Making a Library!
    a very small library

    View Slide

  105. Download Intellij EAP
    https://www.jetbrains.com/idea/nextversion/

    View Slide

  106. View Slide

  107. View Slide

  108. View Slide

  109. View Slide

  110. targets {
    fromPreset(presets.jvm, 'jvm')
    // This preset is for iPhone emulator
    // Switch here to presets.iosArm64 to build library for iPhone device
    fromPreset(presets.iosX64, 'ios') {
    compilations.main.outputKinds('FRAMEWORK')
    }
    }

    View Slide

  111. targets {
    fromPreset(presets.jvm, 'jvm')
    // This preset is for iPhone emulator
    // Switch here to presets.iosArm64 to build library for iPhone device
    fromPreset(presets.macosX64, 'ios') /*{
    compilations.main.outputKinds('FRAMEWORK')
    }*/
    }

    View Slide

  112. targets {
    fromPreset(presets.jvm, 'jvm')
    fromPreset(presets.js, 'js')
    fromPreset(presets.macosX64, 'macos')
    fromPreset(presets.iosX64, 'iosX64')
    fromPreset(presets.iosArm32, 'iosArm32')
    fromPreset(presets.iosArm64, 'iosArm64')
    }

    View Slide

  113. Live Coding is a Bad Idea
    don’t be a hero

    View Slide

  114. Go to Github
    https://github.com/kpgalligan/MyNewLibrary

    View Slide

  115. Much More
    obviously

    View Slide

  116. Libraries Needed
    • File Access
    • Date (310 port)
    • Mocking (and other test support)
    • Reactive
    • UI State

    View Slide

  117. Getting Started

    View Slide

  118. Join Kotlin Slack
    https://slack.kotlinlang.org

    View Slide

  119. Sample Apps
    the conference apps

    View Slide

  120. https://github.com/touchlab/DroidconKotlin/

    View Slide

  121. Sample Libraries
    Stately
    SQLDelight

    View Slide

  122. Office Hours - Kotlin Multiplatform
    2pm in Coblenz

    View Slide

  123. View Slide

  124. [email protected]
    @kpgalligan

    View Slide

  125. [email protected]
    @kpgalligan
    Join the team
    !

    View Slide