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

Kotlin Multiplatform Intro

Kotlin Multiplatform Intro

Introduction to Kotlin Multiplatform, at Oredev.

Kevin Galligan

November 06, 2019
Tweet

More Decks by Kevin Galligan

Other Decks in Programming

Transcript

  1. Kotlin Multiplatform Intro
    Kevin Galligan

    View Slide

  2. Touchlab

    View Slide

  3. View Slide

  4. What is Kotlin Multiplatform?

    View Slide

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

  6. 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.
    Oh, and JetBrains!

    View Slide

  7. Optional Sharing
    Low risk. No Big Decisions.

    View Slide

  8. Natively Integrated
    smooth interop

    View Slide

  9. open source

    View Slide

  10. Code Sharing
    not “cross platform”

    View Slide

  11. popular

    View Slide

  12. popular

    View Slide

  13. popular

    View Slide

  14. modern

    View Slide

  15. Not UI
    well, not necessarily UI

    View Slide

  16. -Kevin Galligan
    “Shared UI is a history of pain and failure.
    Shared logic is the history of computers.”

    View Slide

  17. Many Platforms

    View Slide

  18. Common
    JVM JS Native
    iOS Mac
    Linux Windows
    Android/NDK Wasm
    Others…
    Java-6 Java-8
    Android
    Browser
    Node

    View Slide

  19. IDE and Tooling
    JetBrains makes it

    View Slide

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

    View Slide

  21. mobile is easy

    View Slide

  22. mobile is easy

    View Slide

  23. Technically, Though
    what is it?

    View Slide

  24. Common
    JVM JS Native

    View Slide

  25. Common
    JVM JS Native
    iOS Mac
    Linux Windows
    Android/NDK Wasm
    Others…
    Java-6 Java-8
    Android
    Browser
    Node

    View Slide

  26. Common
    JVM JS Native
    iOS Mac
    Linux Windows
    Android/NDK Wasm
    Others…
    Java-6 Java-8
    Android
    Browser
    Node

    View Slide

  27. Common
    JVM JS Native
    iOS Mac
    Linux Windows
    Android/NDK Wasm
    Others…
    Java-6 Java-8
    Android
    Browser
    Node

    View Slide

  28. Common
    JVM JS Native
    iOS Mac
    Linux Windows
    Android/NDK Wasm
    Others…
    Java-6 Java-8
    Android
    Browser
    Node

    View Slide

  29. Common
    JVM JS Native
    iOS Mac
    Linux Windows
    Android/NDK Wasm
    Others…
    Java-6 Java-8
    Android
    Browser
    Node

    View Slide

  30. Common
    JVM Native
    iOS Mac
    Linux Windows
    Android/NDK Wasm
    Others…
    Java-6 Java-8
    Android

    View Slide

  31. Common
    JVM Native

    View Slide

  32. Common
    JVM Native

    View Slide

  33. Common
    JVM Native
    iOS

    View Slide

  34. Common
    JVM iOS

    View Slide

  35. Common
    JVM iOS
    Framework

    View Slide

  36. Common
    JVM iOS
    Framework
    Android

    View Slide

  37. Common
    Android iOS
    Framework

    View Slide

  38. Common
    Android iOS
    Framework
    Android
    Stuff
    iOS
    Stuff

    View Slide

  39. Status

    View Slide

  40. Q3
    Q2 Q4 Q1 Q2
    2018 2019
    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

  41. Q3
    Q2 Q4 Q1 Q2
    2019 2020
    v1.3.30
    v1.3.40
    v1.3.50
    v1.3.60?
    MT Coroutines? Many Threading Libraries
    Many Threading Blog Posts
    Increased Production Adoption
    Improved Tooling Stability
    Compiler Plugins?
    Date/Time?

    View Slide

  42. Q3
    Q2 Q4 Q1 Q2
    2019 2020
    v1.3.30
    v1.3.40
    v1.3.50
    v1.3.60?
    MT Coroutines?
    Mainstream
    Date/Time?

    View Slide

  43. Is This Ready?
    that’s more about your team

    View Slide

  44. Multithreaded Coroutines
    need to change my slides

    View Slide

  45. Sharing Code

    View Slide

  46. Kotlin Embraces “Native”
    the interop story is really good

    View Slide

  47. expect/actual

    View Slide

  48. //In common code
    expect val isMainThread: Boolean

    View Slide

  49. //In common code
    expect val isMainThread: Boolean
    //In Android/JVM
    actual val isMainThread: Boolean
    get() = Looper.getMainLooper() === Looper.myLooper()

    View Slide

  50. //In common code
    expect val isMainThread: Boolean
    //In Android/JVM
    actual val isMainThread: Boolean
    get() = Looper.getMainLooper() === Looper.myLooper()
    //In iOS/native code
    actual val isMainThread: Boolean
    get() = NSThread.isMainThread()

    View Slide

  51. //Value
    expect val isMainThread: Boolean

    View Slide

  52. //Value
    expect val isMainThread: Boolean
    //Function
    expect fun myFun():String

    View Slide

  53. //Value
    expect val isMainThread: Boolean
    //Function
    expect fun myFun():String
    //Class
    expect class MyClass {
    fun heyo(): String
    }

    View Slide

  54. //Value
    expect val isMainThread: Boolean
    //Function
    expect fun myFun():String
    //Class
    expect class MyClass {
    fun heyo(): String
    }
    //Object
    expect object MyObject {
    fun heyo(): String
    }

    View Slide

  55. //Value
    expect val isMainThread: Boolean
    //Function
    expect fun myFun():String
    //Class
    expect class MyClass {
    fun heyo(): String
    }
    //Object
    expect object MyObject {
    fun heyo(): String
    }
    //Annotation
    @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CONSTRUCTOR)
    @Retention(AnnotationRetention.SOURCE)
    expect annotation class Throws(vararg val exceptionClasses: KClassThrowable>)

    View Slide

  56. expect class Sample() {
    fun checkMe(): Int
    }
    expect object Platform {
    val name: String
    }
    fun hello(): String = "Hello from ${Platform.name}"

    View Slide

  57. actual class Sample {
    actual fun checkMe() = 44
    }
    actual object Platform {
    actual val name: String = "Android"
    }

    View Slide

  58. actual class Sample {
    actual fun checkMe() = 7
    }
    actual object Platform {
    actual val name: String = "iOS"
    }

    View Slide

  59. actual typealias
    with great power…

    View Slide

  60. /**
    * Multiplatform AtomicInt implementation
    */
    expect class AtomicInt(initialValue: Int) {
    fun get(): Int
    fun set(newValue: Int)
    fun incrementAndGet(): Int
    fun decrementAndGet(): Int
    fun addAndGet(delta: Int): Int
    fun compareAndSet(expected: Int, new: Int): Boolean
    }

    View Slide

  61. JVM Side?

    View Slide

  62. import java.util.concurrent.atomic.AtomicInteger
    actual typealias AtomicInt = AtomicInteger

    View Slide

  63. Actual needs to match
    because obviously it does

    View Slide

  64. import kotlin.native.concurrent.AtomicInt
    actual class AtomicInt actual constructor(initialValue:Int){
    private val atom = AtomicInt(initialValue)
    actual fun get(): Int = atom.value
    actual fun set(newValue: Int) {
    atom.value = newValue
    }
    actual fun incrementAndGet(): Int = atom.addAndGet(1)
    actual fun decrementAndGet(): Int = atom.addAndGet(-1)
    actual fun addAndGet(delta: Int): Int = atom.addAndGet(delta)
    actual fun compareAndSet(expected: Int, new: Int): Boolean =
    atom.compareAndSet(expected, new)
    }

    View Slide

  65. import kotlin.native.concurrent.AtomicInt
    actual class AtomicInt actual constructor(initialValue:Int){
    private val atom = AtomicInt(initialValue)
    actual fun get(): Int = atom.value
    actual fun set(newValue: Int) {
    atom.value = newValue
    }
    actual fun incrementAndGet(): Int = atom.addAndGet(1)
    actual fun decrementAndGet(): Int = atom.addAndGet(-1)
    actual fun addAndGet(delta: Int): Int = atom.addAndGet(delta)
    actual fun compareAndSet(expected: Int, new: Int): Boolean =
    atom.compareAndSet(expected, new)
    }

    View Slide

  66. library talk

    View Slide

  67. Prefer Interfaces
    for “service” objects

    View Slide

  68. public interface Settings {
    public fun clear()
    public fun remove(key: String)
    public fun hasKey(key: String): Boolean
    public fun putInt(key: String, value: Int)
    public fun getInt(key: String, defaultValue: Int = 0): Int
    public fun getIntOrNull(key: String): Int?
    public fun putLong(key: String, value: Long)
    public fun getLong(key: String, defaultValue: Long = 0): Long
    public fun getLongOrNull(key: String): Long?
    //Etc...
    }
    from https://github.com/russhwolf/multiplatform-settings

    View Slide

  69. expect fun platformSettings():Settings

    View Slide

  70. object ServiceRegistry {
    var sessionizeApi:SessionizeApi by ThreadLocalDelegate()
    var analyticsApi: AnalyticsApi by FrozenDelegate()
    var notificationsApi:NotificationsApi by FrozenDelegate()
    var dbDriver: SqlDriver by FrozenDelegate()
    var cd: CoroutineDispatcher by FrozenDelegate()
    var appSettings: Settings by FrozenDelegate()
    var concurrent: Concurrent by FrozenDelegate()
    var timeZone: String by FrozenDelegate()
    //Etc…
    from https://github.com/touchlab/DroidconKotlin/

    View Slide

  71. Just an Interface
    no expect/actual required

    View Slide

  72. class TestSettings:Settings {
    private val map = frozenHashMap()
    override fun clear() {
    map.clear()
    }
    override fun getBoolean(key: String, defaultValue: Boolean): Bo
    return if(map.containsKey(key)){
    map[key] as Boolean
    }else{
    defaultValue
    }
    }
    //Etc…
    from https://github.com/touchlab/DroidconKotlin/

    View Slide

  73. object ServiceRegistry {
    var sessionizeApi:SessionizeApi by ThreadLocalDelegate()
    var analyticsApi: AnalyticsApi by FrozenDelegate()
    var notificationsApi:NotificationsApi by FrozenDelegate()
    var dbDriver: SqlDriver by FrozenDelegate()
    var cd: CoroutineDispatcher by FrozenDelegate()
    var appSettings: Settings by FrozenDelegate()
    var concurrent: Concurrent by FrozenDelegate()
    var timeZone: String by FrozenDelegate()
    //Etc…
    from https://github.com/touchlab/DroidconKotlin/

    View Slide

  74. koin multiplatform

    View Slide

  75. interface AnalyticsApi {
    fun logEvent(name: String, params: Map)
    }

    View Slide

  76. class AnalyticsApiImpl(val firebaseAnalytics: FirebaseAnalytics) :
    AnalyticsApi {
    override fun logEvent(name: String, params: Map) {
    val bundle = Bundle()
    params.keys.forEach { key ->
    when (val obj = params[key]) {
    is String -> bundle.putString(key, obj)
    is Int -> bundle.putInt(key, obj)
    else -> {
    throw IllegalArgumentException(“…”)
    }
    }
    }
    firebaseAnalytics.logEvent(name, bundle)
    }
    }

    View Slide

  77. class AnalyticsApiMock : AnalyticsApi {
    var logCalled = false
    override fun logEvent(name: String, params: Map) {
    logCalled = true
    }
    }

    View Slide

  78. class FirebaseAnalyticsApi: AnalyticsApi{
    func logEvent(name: String, params: [String : Any]) {
    Analytics.logEvent(name, parameters: params)
    }
    }

    View Slide

  79. serviceRegistry.doInitLambdas(staticFileLoader: loadAsset,
    clLogCallback: csLog,
    softExceptionCallback: softExceptionCallback)
    serviceRegistry.doInitServiceRegistry(sqlDriver: …,
    coroutineDispatcher: UI(),
    settings: FunctionsKt.defaultSettings(),
    concurrent: MainConcurrent(),
    sessionizeApi: SessionizeApiImpl(),
    analyticsApi: …,
    notificationsApi: NotificationsApiImpl(),
    timeZone: timeZone)
    AppContext().doInitAppContext(networkRepo: NetworkRepo(),
    fileRepo: FileRepo(),
    serviceRegistry: serviceRegistry,
    dbHelper: SessionizeDbHelper(),
    notificationsModel: NotificationsModel())

    View Slide

  80. Function args!
    swift friendly

    View Slide

  81. fun initLambdas(
    staticFileLoader: (filePrefix: String, fileType: String)
    -> String?,
    clLogCallback: (s: String) -> Unit,
    softExceptionCallback: (e:Throwable, message:String) -
    >Unit)

    View Slide

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

  83. Hard(er) to test
    tests are Kotlin exe

    View Slide

  84. go.touchlab.co/dcktsrc

    View Slide

  85. go.touchlab.co/dcktsrc

    View Slide

  86. So no expect/actual?
    um…

    View Slide

  87. Minimize expect/actual
    Droidcon App: 9 functions, 2 classes

    View Slide

  88. Minimize expect/actual?
    Firestore SDK: 100+ ‘expect’ instances

    View Slide

  89. Getting Started

    View Slide

  90. Droidcon is OK
    there’s a lot going on, though

    View Slide

  91. JetBrains Doc
    kotlinlang.org/docs/reference/building-mpp-with-gradle.html

    View Slide

  92. “5 minutes”
    go.touchlab.co/kmp5

    View Slide

  93. Kotlin Slack
    https://kotlinlang.org/community/

    View Slide

  94. KMP evaluation kit

    View Slide

  95. KaMP-Kit
    go.touchlab.co/KaMP-Kit

    View Slide

  96. Concurrency
    it’s complicated (with native)

    View Slide

  97. Two Rules
    mutable = 1 thread/immutable = many threads

    View Slide

  98. Frozen
    new state of state

    View Slide

  99. public fun T.freeze(): T {...}

    View Slide

  100. public fun T.freeze(): T {...}

    View Slide

  101. JetBrains Links
    • https://github.com/JetBrains/kotlin-native/blob/
    master/IMMUTABILITY.md
    • https://github.com/JetBrains/kotlin-native/blob/
    master/CONCURRENCY.md
    • https://www.youtube.com/watch?v=nw6YTfEyfO0

    View Slide

  102. go.touchlab.co/knthreads

    View Slide

  103. Kotlinconf

    View Slide

  104. Thanks!
    @kpgalligan
    touchlab.co

    View Slide

  105. Thanks!
    @kpgalligan
    touchlab.co
    Join the team
    !

    View Slide