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

A Multiplatform Triathlon

cmota
October 17, 2020

A Multiplatform Triathlon

For several years now, that we’ve been trying to find new solutions on how we could develop a single project and run it on all platforms. We’ve been seeing all types of solutions: some are web-based, others require you to learn a new language, and others even may require that you pay a monthly fee to use. It also comes with the drawback that all your UI depends on the framework implementation. So, if there’s a native update you’ll need to wait until someone rewrites the widgets for you to update your app.

Kotlin multiplatform gives us a new solution. It focuses on sharing your business logic across all platforms, leaving the UI to be implemented natively. Since it’s Kotlin, you can take full advantage of its language features - concise, safe, etc. Moreover, if you’re coming from android you might already be familiarised with Kotlin and even if you’re an iOS developer you’ll see that it’s quite similar to Swift.

Join me on this triathlon and let’s go through Android, iOS, and Web in under 30 minutes.

cmota

October 17, 2020
Tweet

More Decks by cmota

Other Decks in Programming

Transcript

  1. @cafonsomota
    Kotlin through Android, iOS and Web
    A Multiplatform Triathlon

    View full-size slide

  2. Android Dev Lead @WITSoftware
    Founder @GDGCoimbra and co-founder @Kotlin_Knights
    ✍ Author @rwenderlich and @opinioesonline
    Podcaster wannabe
    Loves travel, photography and running
    @cafonsomota

    View full-size slide

  3. MATERIALS
    speakerdeck.com/cmota/a-multiplatform-triathlon
    github.com/cmota/kmp-a-multiplatform-triathlon
    cmota.github.io/kmp-codelabs
    @cafonsomota

    View full-size slide

  4. @cafonsomota
    dev-reactions/commitstrip/unrealist-mobile-dev-dream

    View full-size slide

  5. 2008 2012 2018
    2013
    2011 2017
    2006 2010
    2009 2015
    * Kotlin Multiplatform
    *

    View full-size slide

  6. - Small learning curve for web developers
    - Smaller team
    - Typically half of the cost needed on native
    - Less time (working hours) to built
    (advantages)
    Cross-platform
    to test
    to fix issues (hopefully not framework specific )

    View full-size slide

  7. - Chained to the framework implementation of UI
    - new updates from the OS will take time to adopt
    - Performance is not the same
    - Some native code might need to be written
    - OS/ device features are dependent on the fw support
    - Dart (flutter) is not widely used language (for now)
    - Committed to one framework/ language
    (disadvantages)
    Cross-platform

    View full-size slide

  8. what about Kotlin Multiplatform?

    View full-size slide

  9. shares application logic
    doesn’t share the application UI
    kotlin
    multiplatform

    View full-size slide

  10. - Chained to the framework implementation of UI
    - new updates from the OS will take time to adopt
    - Performance is not the same
    - Some native code might need to be written
    - OS/ device features are dependent on the fw support
    - Dart (flutter) is not widely used language (for now)
    - Committed to one framework/ language
    (disadvantages)
    Cross-platform

    View full-size slide

  11. - Chained to the framework implementation of UI
    - new updates from the OS will take time to adopt
    - Performance is not the same
    - Some native code might need to be written
    - OS/ device features are dependent on the fw support
    - Dart (flutter) is not widely used language (for now)
    - Committed to one framework/ language
    Cross-multiplatform
    UI is developed natively
    nope, UI is developed natively

    View full-size slide

  12. - Chained to the framework implementation of UI
    - new updates from the OS will take time to adopt
    - Performance is not the same
    - Some native code might need to be written
    - OS/ device features are dependent on the fw support
    - Dart (flutter) is not widely used language (for now)
    - Committed to one framework/ language
    Cross-multiplatform
    nope, UI is developed natively
    UI is developed natively
    always arguable, but you’re leaving UI to native, so…

    View full-size slide

  13. - Chained to the framework implementation of UI
    - new updates from the OS will take time to adopt
    - Performance is not the same
    - Some native code might need to be written
    - OS/ device features are dependent on the fw support
    - Dart (flutter) is not widely used language (for now)
    - Committed to one framework/ language
    Cross-multiplatform
    nope, UI is developed natively
    yes, the UI
    UI is developed natively
    always arguable, but you’re leaving UI to native, so…

    View full-size slide

  14. - Chained to the framework implementation of UI
    - new updates from the OS will take time to adopt
    - Performance is not the same
    - Some native code might need to be written
    - OS/ device features are dependent on the fw support
    - Dart (flutter) is not widely used language (for now)
    - Committed to one framework/ language
    Cross-multiplatform
    nope, UI is developed natively
    yes, the UI
    UI is developed natively
    you have direct access
    to them; although if on
    shared module it might
    give you extra effort
    always arguable, but you’re leaving UI to native, so…

    View full-size slide

  15. - Chained to the framework implementation of UI
    - new updates from the OS will take time to adopt
    - Performance is not the same
    - Some native code might need to be written
    - OS/ device features are dependent on the fw support
    - Dart (flutter) is not widely used language (for now)
    - Committed to one framework/ language
    Cross-multiplatform
    nope, UI is developed natively
    yes, the UI
    UI is developed natively
    kotlin, is one of the most
    trending languages nowadays,
    with strong community support
    always arguable, but you’re leaving UI to native, so…
    you have direct access
    to them; although if on
    shared module it might
    give you extra effort

    View full-size slide

  16. (advantages)
    - Language features
    - Kotlin first!
    - Low risk
    - You decide what’s worth to share across platforms
    - Interoperability
    - Consistency across platforms
    - Strong investment from JetBrains and community support
    Kotlin Multiplatform server web native
    android desktop
    jvmMain jsMain androidMain iosMain macosX64Main
    linuxX64Main
    mingwX64Main

    View full-size slide

  17. - It’s in alpha
    - Although we keep seeing new projects in production
    - Space was announced during Kotlin Conf 19 (web, desktop and mobile)
    (apps in production)
    Kotlin Multiplatform
    Space
    Adapted from: KotlinConf 2019: Opening Keynote by Andrey Breslav
    Full KMP
    Cash App
    Shares:
    business logic
    Yandex Maps
    Shares:
    business logic,
    wrappers for C++
    libraries
    PlanGrid Planboard
    Workspace
    Shares:
    business logic,
    sync logic,
    mgmnt offline data
    Shares:
    business logic,
    networking,
    offline caching lyrs
    Shares:
    business logic

    View full-size slide

  18. Kotlin Multiplatform

    View full-size slide

  19. K
    otlin multiplatform
    using kotlin in projects
    that target more than
    one platform

    View full-size slide

  20. network network
    parser parser
    model model
    presentation presentation
    model
    parser
    network
    view
    presentation
    view view
    presentation presentation
    web
    iOS
    android

    View full-size slide

  21. business logic business logic business logic
    model
    parser
    network
    presentation
    model
    parser
    network
    presentation
    model
    parser
    network
    presentation
    view view view
    web
    iOS
    android

    View full-size slide

  22. model
    parser
    network
    presentation
    common
    java/kotlin objective-c/ swift (kotlin) JS
    view view view
    web
    iOS
    android

    View full-size slide

  23. dev-reactions/it-works.gif

    View full-size slide

  24. but how can different platforms
    expect to communicate?

    View full-size slide

  25. declared at common module
    expect
    declared at android module
    actual
    declared at iOS module
    declared at …

    View full-size slide

  26. in which platform is my application
    running?

    View full-size slide

  27. expect object Platform {

    val name: String

    }

    commonMain
    shared/src/commonMain/sample/Platform.kt

    View full-size slide

  28. expect object Platform {

    val name: String

    }

    shared/src/commonMain/sample/Platform.kt
    commonMain
    - define actual on android
    - define actual on iOS
    - define actual on web
    targets: android, iOS and the web

    View full-size slide

  29. import android.os.Build.MANUFACTURER

    actual object Platform {

    actual val name: String = $MANUFACTURER

    }

    shared/src/androidMain/sample/Platform.kt
    Platform-dependent code

    View full-size slide

  30. import android.os.Build.MANUFACTURER

    actual object Platform {

    actual val name: String = $MANUFACTURER

    }

    shared/src/androidMain/sample/Platform.kt
    Platform-dependent code

    View full-size slide

  31. import android.os.Build.MANUFACTURER

    actual object Platform {

    actual val name: String = $MANUFACTURER

    }

    shared/src/androidMain/sample/Platform.kt
    import platform.UIKit.UIDevice

    actual object Platform {

    actual val name: String = UIDevice.currentDevice().name

    }

    Platform-dependent code
    shared/src/iosMain/sample/Platform.kt

    View full-size slide

  32. import android.os.Build.MANUFACTURER

    actual object Platform {

    actual val name: String = $MANUFACTURER

    }

    shared/src/androidMain/sample/Platform.kt
    import platform.UIKit.UIDevice

    actual object Platform {

    actual val name: String = UIDevice.currentDevice().name

    }

    Platform-dependent code
    import kotlinx.browser.window

    actual object Platform {

    actual val name: String = window.navigator.useragent

    }

    shared/src/iosMain/sample/Platform.kt
    shared/src/jsMain/sample/Platform.kt

    View full-size slide

  33. Platform-dependent code
    Hello Google-Chrome
    Hello Pixel-XL Hello iOS-Simulator

    View full-size slide

  34. androidApp/
    iosApp/
    shared/
    web/

    View full-size slide

  35. androidApp/
    iosApp/
    shared/
    web/
    main/
    MainActivity.kt
    src/
    java/
    app.package.name/
    activities/

    View full-size slide

  36. src/
    commonMain/
    commonTest/
    shared/
    androidApp/
    iosApp/
    web/

    View full-size slide

  37. src/
    commonMain/
    commonTest/
    shared/

    androidApp/
    iosApp/
    web/

    View full-size slide

  38. androidMain
    androidTest

    src/
    commonMain/
    commonTest/
    shared/
    androidApp/
    iosApp/
    web/

    View full-size slide



  39. src/
    commonMain/
    commonTest/
    shared/
    androidMain
    androidTest
    androidApp/
    iosApp/
    web/

    View full-size slide



  40. src/
    commonMain/
    commonTest/
    shared/
    androidMain
    androidTest
    iosMain/
    iosTest/
    jsMain/
    jsTest/


    View full-size slide

  41. Let’s get everything
    together

    View full-size slide

  42. build.gradle.kts (:shared)
    kotlin {



    sourceSets {

    val commonMain by getting {

    dependencies {



    implementation("io.ktor:ktor-client-core:1.4.0")

    implementation("io.ktor:ktor-client-json:1.4.0")

    implementation("io.ktor:ktor-client-serialization:1.4.1")

    implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0")

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")

    implementation("com.squareup.sqldelight:runtime:1.4.3")

    implementation("com.russhwolf:multiplatform-settings:0.6.2")

    }

    }

    }

    }

    View full-size slide

  43. build.gradle.kts (:shared)
    kotlin {

    android()



    sourceSets {

    val commonMain by getting {

    dependencies {



    implementation("io.ktor:ktor-client-core:1.4.0")

    implementation("io.ktor:ktor-client-json:1.4.0")

    implementation("io.ktor:ktor-client-serialization:1.4.1")

    implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0")

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")

    implementation("com.squareup.sqldelight:runtime:1.4.3")

    implementation("com.russhwolf:multiplatform-settings:0.6.2")

    }

    val androidMain by getting {
    dependencies {
    implementation("androidx.preference:preference:1.1.1")
    implementation("io.ktor:ktor-client-android:1.4.0")
    implementation("io.ktor:ktor-client-okhttp:1.4.0")
    implementation("com.squareup.sqldelight:android-driver:1.4.3")
    }
    }
    }

    View full-size slide

  44. build.gradle.kts (:shared)
    kotlin {

    android()

    val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false
    if (onPhone) {
    iosArm64("ios")
    } else {
    iosX64("ios")
    }
    cocoapods {
    summary = "Some description for a Kotlin/Native module"
    homepage = "Link to a Kotlin/Native module homepage"
    frameworkName = "shared"
    ios.deploymentTarget = "13.2"
    }
    sourceSets {

    val commonMain by getting {

    dependencies {



    implementation("io.ktor:ktor-client-core:1.4.0")

    implementation("io.ktor:ktor-client-json:1.4.0")

    implementation("io.ktor:ktor-client-serialization:1.4.1")

    implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0")

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")

    View full-size slide

  45. build.gradle.kts (:shared)
    kotlin {

    android()

    val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false
    if (onPhone) {
    iosArm64("ios")
    } else {
    iosX64("ios")
    }
    cocoapods {
    summary = "Some description for a Kotlin/Native module"
    homepage = "Link to a Kotlin/Native module homepage"
    frameworkName = "shared"
    ios.deploymentTarget = "13.2"
    }
    sourceSets {

    val commonMain by getting {

    dependencies {



    implementation("io.ktor:ktor-client-core:1.4.0")

    implementation("io.ktor:ktor-client-json:1.4.0")

    implementation("io.ktor:ktor-client-serialization:1.4.1")

    implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0")

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")

    implementation("com.squareup.sqldelight:runtime:1.4.3")

    implementation("com.russhwolf:multiplatform-settings:0.6.2")

    }

    val androidMain by getting {
    dependencies {
    implementation("androidx.preference:preference:1.1.1")
    implementation("io.ktor:ktor-client-android:1.4.0")
    implementation("io.ktor:ktor-client-okhttp:1.4.0")
    implementation("com.squareup.sqldelight:android-driver:1.4.3")
    }
    }
    }

    val iosMain by getting {
    dependencies {
    implementation("io.ktor:ktor-client-ios:1.4.0")
    implementation("com.squareup.sqldelight:native-driver:1.4.3")
    }
    }
    }

    }

    View full-size slide

  46. build.gradle.kts (:shared)
    kotlin {

    android()

    val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false
    if (onPhone) {
    iosArm64("ios")
    } else {
    iosX64("ios")
    }
    cocoapods {
    summary = "Some description for a Kotlin/Native module"
    homepage = "Link to a Kotlin/Native module homepage"
    frameworkName = "shared"
    ios.deploymentTarget = "13.2"
    }
    js {
    browser {
    }
    }
    sourceSets {

    val commonMain by getting {

    dependencies {



    implementation("io.ktor:ktor-client-core:1.4.0")

    implementation("io.ktor:ktor-client-json:1.4.0")

    implementation("io.ktor:ktor-client-serialization:1.4.1")

    implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0")

    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")

    implementation("com.squareup.sqldelight:runtime:1.4.3")

    implementation("com.russhwolf:multiplatform-settings:0.6.2")

    }

    val androidMain by getting {
    dependencies {
    implementation("androidx.preference:preference:1.1.1")
    implementation("io.ktor:ktor-client-android:1.4.0")
    implementation("io.ktor:ktor-client-okhttp:1.4.0")
    implementation("com.squareup.sqldelight:android-driver:1.4.3")
    }
    }
    }

    val iosMain by getting {
    dependencies {
    implementation("io.ktor:ktor-client-ios:1.4.0")
    implementation("com.squareup.sqldelight:native-driver:1.4.3")
    }
    }
    val jsMain by getting {
    dependencies {
    implementation("io.ktor:ktor-client-js:1.4.0")
    }
    }
    }

    }

    View full-size slide

  47. *.kt
    common
    expect
    JVM
    actual
    *.kt, *.java, *.jar
    Native
    actual
    *.kt, *C, Swift, Framework
    JS
    actual
    *.kt, *.js, NPM

    View full-size slide

  48. dev-reactions/god-mode.gif

    View full-size slide

  49. - User interface
    - RecyclerView and more (Android)
    - UITableViewController and more (iOS)
    - Depends on the framework used (web)
    - Network request
    - Parse response objects
    - Store on local database
    - Use system preferences
    Project structure

    View full-size slide

  50. android iOS
    Room CoreData
    Retrofit Alamofire
    GSON/ Moshi JSONSerialization
    MVP, MVVM, MVI MVVM, ELM
    RxJava RxSwift
    Tests Tests
    Activity UIViewController
    RecyclerView UITableView
    web
    There are a lot of
    different frameworks
    here…
    - redux
    - Fetch
    - json-api-serializer

    View full-size slide

  51. SQLDelight
    ktor
    kotlinx.serialization
    MVP
    kotlinx.coroutines
    kotlin.test
    Activity UIViewController
    RecyclerView UITableView
    android iOS web

    View full-size slide

  52. multiplatform
    ktor
    SQLDelight
    model
    kotlinx.serialization
    GetConferences
    SQLDriver dispatcher
    view
    presenter
    Settings
    presenter

    View full-size slide

  53. multiplatform
    network
    database
    model
    parser
    domain
    platform specific
    view
    presenter
    presenter

    View full-size slide

  54. Network
    class ConferencesAPI() {

    private val client = HttpClient()

    suspend fun fetchConfs() = client.get("$URL")
    src/commonMain/data/ConferencesAPI.kt
    ktor
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model

    View full-size slide

  55. src/commonMain/domain/model/Conference.kt
    model
    @Serializable

    data class Conference (

    val name: String,

    val city: String,

    val country: String,

    val date: String,

    val logo: String,

    val website: String,

    val status: String)
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model

    View full-size slide

  56. domain
    class GetConferences(val api: ConferencesAPI) {

    suspend operator fun invoke(

    onSuccess: (List) "-> Unit,

    onFailure: (Exception) "-> Unit) {

    val result = api.fetchConfs()

    val confs = Json.decodeFromString>(result)

    coroutineScope {

    onSuccess(confs)

    }

    ""...
    }
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model
    src/commonMain/domain/GetConferences.kt

    View full-size slide

  57. domain
    class GetConferences(val api: ConferencesAPI) {

    suspend operator fun invoke(

    onSuccess: (List) "-> Unit,

    onFailure: (Exception) "-> Unit) {

    val result = api.fetchConfs()

    val confs = Json.decodeFromString>(result)

    coroutineScope {

    onSuccess(confs)

    }

    ""...
    }
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model
    src/commonMain/domain/GetConferences.kt

    View full-size slide

  58. domain
    class GetConferences(val api: ConferencesAPI) {

    suspend operator fun invoke(

    onSuccess: (List) "-> Unit,

    onFailure: (Exception) "-> Unit) {

    val result = api.fetchConfs()

    val confs = Json.decodeFromString>(result)

    coroutineScope {

    onSuccess(confs)

    }

    ""...
    }
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model
    src/commonMain/domain/GetConferences.kt

    View full-size slide

  59. domain
    class GetConferences(val api: ConferencesAPI) {

    suspend operator fun invoke(

    onSuccess: (List) "-> Unit,

    onFailure: (Exception) "-> Unit) {

    val result = api.fetchConfs()

    val confs = Json.decodeFromString>(result)

    coroutineScope {

    onSuccess(confs)

    }

    ""...
    }
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model
    src/commonMain/domain/GetConferences.kt

    View full-size slide

  60. src/commonMain/presentation/ConfsListPresenter.kt
    class ConfsListPresenter(val conferences: GetConferences) {

    lateinit var view: IConferenceData

    fun attachView(currView: IConferenceData) {

    view = currView

    fetchConfsList()

    }





    presenter
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model

    View full-size slide

  61. src/commonMain/presentation/ConfsListPresenter.kt
    class ConfsListPresenter(val conferences: GetConferences) {

    lateinit var view: IConferenceData

    fun attachView(currView: IConferenceData) {

    view = currView

    fetchConfsList()

    }





    presenter
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model

    View full-size slide

  62. src/commonMain/presentation/cb/IConferenceData.kt
    interface IConferenceData {

    fun onConferenceDataFetched(confs: List)

    fun onConferenceDataFailed(e: Exception)

    }
    presenter
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model

    View full-size slide

  63. src/commonMain/presentation/ConfsListPresenter.kt
    class ConfsListPresenter(val conferences: GetConferences) {

    lateinit var view: IConferenceData

    fun attachView(currView: IConferenceData) {

    view = currView

    fetchConfsList()

    }

    private fun fetchConfsList() {

    PresenterCoroutineScope(coroutineContext).launch {

    conferences(

    onSuccess = { view"?.onConferenceDataFetched(it) },

    onFailure = { view"?.onConferenceDataFailed(it) })

    presenter
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model

    View full-size slide

  64. object ServiceLocator {

    private val conferenceAPI by lazy {

    ConferencesAPI()

    }

    private val getConferences: GetConferences

    get() = GetConferences(conferenceAPI)

    val getConfsPresenter: ConfsListPresenter

    get() = ConfsListPresenter(getConferences)



    presenter
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model
    src/commonMain/ServiceLocator.kt

    View full-size slide

  65. object ServiceLocator {

    private val conferenceAPI by lazy {

    ConferencesAPI()

    }

    private val getConferences: GetConferences

    get() = GetConferences(conferenceAPI)

    val getConfsPresenter: ConfsListPresenter

    get() = ConfsListPresenter(getConferences)



    presenter
    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model
    src/commonMain/ServiceLocator.kt

    View full-size slide

  66. src/commonAndroid/presenter/activities/MainActivity.kt
    android
    class MainActivity : AppCompatActivity(), IConferencesData {

    val presenter by lazy { ServiceLocator.getConfsPresenter }

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    presenter.attachView(this)

    }

    override fun onConferenceDataFetched(list: List){

    setup(list)



    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model

    View full-size slide

  67. src/commonAndroid/presenter/activities/MainActivity.kt
    android
    class MainActivity : AppCompatActivity(), IConferencesData {

    val presenter by lazy { ServiceLocator.getConfsPresenter }

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    presenter.attachView(this)

    }

    override fun onConferenceDataFetched(list: List){

    setup(list)



    multiplatform
    network
    database
    android
    parser
    view
    platform
    specific
    presenter
    presenter
    domain
    model

    View full-size slide

  68. @cafonsomota
    > Configure project :shared

    Kotlin Multiplatform Projects are an Alpha feature.
    cmota.github.io/kmp-codelabs

    View full-size slide

  69. - Start small, don’t try to reach 100% of shared logic
    - perhaps by writing common tests to gain familiarity
    - one module at a time (network, database, image processing, etc.)
    - You can spend some time resolving compilation issues
    - specially if you try to target all platforms
    (how to start?)
    Kotlin Multiplatform
    - It’s in alpha
    - Lookout for plugins/ libraries updates

    View full-size slide

  70. - evangelize the iOS team
    - select one module where something is more complex on iOS than on Android
    - KMP is not intended to share the UI
    - iOS developers can focus on Swift and frontend features
    - Cross platform solutions like Flutter or React Native they need to use Dart or JS
    - not possible to debug kotlin from Xcode
    - plugin for Xcode from TouchLab/ support mentioned on Kotlin Conf 19
    (how to start?)
    Kotlin Multiplatform

    View full-size slide

  71. - Strong community
    - A lot of people are using kotlin nowadays
    - Google and JetBrains are pushing Kotlin in
    - You can ask questions directly on https://kotlinlang.slack.com
    - 2x faster to develop your features business logic
    - 2x faster writing unit tests
    - One tech stack
    - Consistency across platforms
    (conclusions)
    Kotlin Multiplatform

    View full-size slide

  72. - Ktor (networking)
    - Kotlinx.Coroutines
    - Kotlinx.Serialization
    - SqlDelight (database)
    - Multiplatform Settings
    - Stately (state management)
    (libraries)
    More information

    View full-size slide

  73. - .../JetBrains/kotlinconf-app
    - .../touchlab/DroidconKotlin
    - …/touchlab/KaMPKit
    - .../wojtek-kalicinski/sudoku-android
    - .../joreilly/PeopleInSpace
    - .../jonnyzzz/kotlin-game-of-life
    - .../MarcinMoskala/WorkoutMPP
    (cool projects to follow at GitHub)
    More information

    View full-size slide

  74. - Kotlin Slack
    - Kotlin by:
    https://jakewharton.com/presentations/
    https://touchlab.co/kamp-kit-touchlab/
    https://kmp.icerock.dev/
    - Kotlin Conf 2019 videos
    https://www.youtube.com/watch?v=0xKTM0A8gdI
    - Kotlin 1.4 Online Event
    https://www.youtube.com/watch?v=PW-jkOLucjM
    https://www.youtube.com/watch?v=5QPPZV04-50
    https://www.youtube.com/watch?v=KObxmllDzPY
    (useful links)
    More information

    View full-size slide

  75. @cafonsomota
    cmota.github.io/kmp-codelabs/
    cmota.github.io/kmp-codelabs

    View full-size slide

  76. @cafonsomota
    speakerdeck.com/cmota/a-multiplatform-triathlon
    github.com/cmota/kmp-a-multiplatform-triathlon
    cmota.github.io/kmp-codelabs

    View full-size slide