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 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 Slide

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

    View Slide

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

    View Slide

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

    View 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 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 Slide

  8. View Slide

  9. what about Kotlin Multiplatform?

    View Slide

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

    View 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
    (disadvantages)
    Cross-platform

    View 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
    UI is developed natively
    nope, UI is developed natively

    View 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
    UI is developed natively
    always arguable, but you’re leaving UI to native, so…

    View 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
    always arguable, but you’re leaving UI to native, so…

    View 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
    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 Slide

  16. - 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 Slide

  17. (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 Slide

  18. - 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 Slide

  19. Kotlin Multiplatform

    View Slide

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

    View Slide

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

    View Slide

  22. 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 Slide

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

    View Slide

  24. dev-reactions/it-works.gif

    View Slide

  25. View Slide

  26. but how can different platforms
    expect to communicate?

    View Slide

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

    View Slide

  28. in which platform is my application
    running?

    View Slide

  29. expect object Platform {

    val name: String

    }

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

    View Slide

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

  31. import android.os.Build.MANUFACTURER

    actual object Platform {

    actual val name: String = $MANUFACTURER

    }

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

    View Slide

  32. import android.os.Build.MANUFACTURER

    actual object Platform {

    actual val name: String = $MANUFACTURER

    }

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

    View Slide

  33. 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 Slide

  34. 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 Slide

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

    View Slide

  36. androidApp/
    iosApp/
    shared/
    web/

    View Slide

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

    View Slide

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

    View Slide

  39. src/
    commonMain/
    commonTest/
    shared/

    androidApp/
    iosApp/
    web/

    View Slide

  40. androidMain
    androidTest

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

    View Slide



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

    View Slide



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


    View Slide

  43. Let’s get everything
    together

    View Slide

  44. 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 Slide

  45. 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 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"
    }
    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 Slide

  47. 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 Slide

  48. 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 Slide

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

    View Slide

  50. dev-reactions/god-mode.gif

    View Slide

  51. View Slide

  52. View Slide

  53. View Slide

  54. - 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 Slide

  55. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

  59. 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 Slide

  60. 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 Slide

  61. 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 Slide

  62. 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 Slide

  63. 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 Slide

  64. 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 Slide

  65. 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 Slide

  66. 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 Slide

  67. 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 Slide

  68. 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 Slide

  69. 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 Slide

  70. 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 Slide

  71. 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 Slide

  72. 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 Slide

  73. @cafonsomota
    > Configure project :shared

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

    View Slide

  74. - 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 Slide

  75. - 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 Slide

  76. - 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 Slide

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

    View Slide

  78. - .../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 Slide

  79. - 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 Slide

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

    View Slide

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

    View Slide