$30 off During Our Annual Pro Sale. View Details »

KMP4FREE in 2023 @ Droidcon NYC 2022

Sam Edwards
September 02, 2022

KMP4FREE in 2023 @ Droidcon NYC 2022

Video: https://www.droidcon.com/2022/09/29/kmp4free-in-2023/

At Droidcon SF 2022, I first publicly announced the KMP4FREE Gradle Plugin and how it can be used to seamlessly toggle between the Kotlin JVM and Kotlin Multiplatform Plugins with a single line change. In this talk I'll show you what this will look like over the next year, and more real world examples of how this Gradle Plugin can be used. KMP4FREE can help you can start making your Kotlin code Multiplatform Compatible, today!

Sam Edwards

September 02, 2022
Tweet

More Decks by Sam Edwards

Other Decks in Technology

Transcript

  1. Sam Edwards - @HandstandSam
    Kotlin & Android GDE @ Dropbox
    KMP4FREE
    in 2023
    Droidcon NYC 2022

    View Slide

  2. Sam Edwards - @HandstandSam
    Kotlin & Android GDE @ Dropbox
    Multiplaformization
    in 2023
    Droidcon NYC 2022

    View Slide

  3. https://twitter.com/relizarov/status/1564288780261068802
    KMP4FREE is a Tool to help with Multiplatformization

    View Slide

  4. K 4FREE
    MP

    View Slide

  5. Primary Goals of KMP4FREE
    • No Risk, Single Line Change to Enable Multiplatform

    • Support JVM & Multiplatform Con
    fi
    g

    • Allow Experimentation and Possible Migration to Multiplatform

    View Slide

  6. KMP4FREE Work?
    How Does

    View Slide

  7. JVM Project Structure
    plugins {
    kotlin("jvm")
    }
    dependencies {
    implementation(libs.kotlin.stdlib)
    implementation(project(":samples:jvm_kmp4free"))
    testImplementation(libs.kotlin.test.common)
    testImplementation(libs.truth)
    }

    View Slide

  8. JVM Project Structure
    plugins {
    id("com.handstandsam.kmp4free")
    }
    dependencies {
    implementation(libs.kotlin.stdlib)
    implementation(project(":samples:jvm_kmp4free"))
    testImplementation(libs.kotlin.test.common)
    testImplementation(libs.truth)
    }

    View Slide

  9. Enable/Disable with Property
    # gradle.properties
    kmp4free=true
    or
    ./gradlew … -Pkmp4free=true

    View Slide

  10. JVM Project Structure

    View Slide

  11. JVM Project Structure

    View Slide

  12. JVM Project Structure
    kmp4free=false

    View Slide

  13. JVM Project Structure
    kmp4free=true
    kmp4free=false

    View Slide

  14. JVM Project Structure
    kmp4free=true
    kmp4free=false

    View Slide

  15. JVM Project Structure
    kmp4free=true
    kmp4free=false

    View Slide

  16. Source Set and Con
    fi
    guration Mapping
    How?

    View Slide

  17. kmp4free=true
    • SourceSet Mapping
    • src/main ➡ src/commonMain

    • src/test ➡ src/jvmTest

    • Con
    fi
    guration Mapping
    • implementation ➡ commonMainImplementation

    • testImplementation ➡ jvmTestImplementation

    • Task Aliasing
    • :module:test ➡ :module:jvmTest

    View Slide

  18. kmp4free=false
    • SourceSet Mapping
    • src/commonMain ➡ src/main

    • src/jvmMain ➡ src/main

    • src/commonTest ➡ src/test

    • src/jvmTest ➡ src/test

    • Con
    fi
    guration Mapping
    • commonMainImplementation ➡ implementation

    • commonTestImplementation ➡ testImplementation

    • jvmTestImplementation ➡ testImplementation

    View Slide

  19. K 4FREE
    MP
    Demo

    View Slide

  20. Enable and Disable Multiplatform Targets?
    Can’t You Just

    View Slide

  21. Compiling Multiplatform Can Be Slow
    https://medium.com/yazio-engineering/speeding-up-kotlin-multiplatform-61ebf8dae560
    grade.properties

    View Slide

  22. Multiplatform Doesn’t Support Con
    fi
    guration Caching (Yet)
    https://youtrack.jetbrains.com/issue/KT-49933/Support-Gradle-Con
    fi
    guration-caching-with-HMPP

    View Slide

  23. Multiplatform Plugin Adds Overhead
    7 Tasks 14 Tasks

    View Slide

  24. Doesn’t Quite Work in Android Studio
    https://issuetracker.google.com/issues/229628556

    View Slide

  25. KMP4FREE?
    Should I Use

    View Slide

  26. but NOT Inde
    fi
    nitely
    It Depends

    View Slide

  27. to Multiplatform
    Finding Your Path

    View Slide

  28. Paths to Multiplatformization
    New Code

    View Slide

  29. Paths to Multiplatformization
    commonMain
    New Code

    View Slide

  30. Paths to Multiplatformization
    commonMain
    Existing Code
    New Code

    View Slide

  31. Paths to Multiplatformization
    Existing Code
    commonMain
    New Code
    KMP4FREE?

    View Slide

  32. Paths to Multiplatformization
    Existing Code
    commonMain
    KMP4FREE?

    View Slide

  33. Shapes The Future
    Every Decision

    View Slide

  34. Has Consequences
    Every Decision

    View Slide

  35. On Kotlin
    Going All In

    View Slide

  36. On Compose
    Going All In

    View Slide

  37. On Multiplatform
    Going All In

    View Slide

  38. Mitigation
    Risk

    View Slide

  39. From The Past
    Learn

    View Slide

  40. 🤔
    https://dropbox.tech/mobile/the-not-so-hidden-cost-of-sharing-code-between-ios-and-android

    View Slide

  41. “By writing code in a non-standard fashion, we
    took on overhead that we would have not had
    to worry about had we stayed with the widely
    used platform defaults. This overhead ended
    up being more expensive than just writing the
    code twice.”

    View Slide

  42. The Overhead of:
    1. Custom frameworks and libraries
    2. Custom development environment
    3. Addressing di
    ff
    erences between the platforms
    4. Training, hiring, and retaining developers

    View Slide

  43. Write Kotlin Multiplatform Code?
    When Will Your Team

    View Slide

  44. with KMP4FREE
    Starting Now

    View Slide

  45. Ship Kotlin Multiplatform Code on iOS?
    When Will Your Team

    View Slide

  46. Maybe Never?
    ¯\_(ツ)_/¯

    View Slide

  47. For Kotlin Multiplatform
    Plant a Seed 🌱

    View Slide

  48. …And It Might Grow 🌲

    View Slide

  49. Plant a Seed 🌱
    • Same Kotlin Code

    • Di
    ff
    erent Gradle plugin

    • Forces modularity

    • Makes developers more likely to try coding in the other platform

    View Slide

  50. Identifying
    Target Projects

    View Slide

  51. Plugins
    Kotlin + Gradle

    View Slide

  52. Gradle Plugin Migration
    kotlin(“android”)

    View Slide

  53. Gradle Plugin Migration
    kotlin(“android”)
    kotlin(“jvm”)

    View Slide

  54. Gradle Plugin Migration
    kotlin(“android”)
    kotlin(“jvm”)
    kotlin(“multiplatform”)

    View Slide

  55. Android Library Kotlin JVM
    Step 1

    View Slide

  56. dependency-analysis-android-gradle-plugin
    github.com/autonomousapps/dependency-analysis-android-gradle-plugin

    View Slide

  57. dependency-analysis-android-gradle-plugin
    1. Gathers Module Data
    2. Runs Decisioning Logic
    3. Shares Actionable Advice

    View Slide

  58. data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    Android Library ➡ JVM Module Advice

    View Slide

  59. data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    Android Library ➡ JVM Module Advice

    View Slide

  60. data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    Android Library ➡ JVM Module Advice

    View Slide

  61. data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    Android Library ➡ JVM Module Advice

    View Slide

  62. data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    Android Library ➡ JVM Module Advice

    View Slide

  63. data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    Android Library ➡ JVM Module Advice

    View Slide

  64. @TypeLabel("android_score")
    @JsonClass(generateAdapter = false)
    data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    @delegate:Transient
    private val score: Float by unsafeLazy {
    var count = 0f
    if (hasAndroidAssets) count += 2
    if (hasAndroidRes) count += 2
    if (usesAndroidClasses) count += 2
    if (hasBuildConfig) count += 0.5f
    if (hasAndroidDependencies) count += 0.5f
    count
    }
    Android Library ➡ JVM Module Advice

    View Slide

  65. @TypeLabel("android_score")
    @JsonClass(generateAdapter = false)
    data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    @delegate:Transient
    private val score: Float by unsafeLazy {
    var count = 0f
    if (hasAndroidAssets) count += 2
    if (hasAndroidRes) count += 2
    if (usesAndroidClasses) count += 2
    if (hasBuildConfig) count += 0.5f
    if (hasAndroidDependencies) count += 0.5f
    count
    }
    Android Library ➡ JVM Module Advice

    View Slide

  66. /** True if this project uses no Android facilities at all. */
    fun shouldBeJvm(): Boolean = score == 0f
    Android Library ➡ JVM Module Advice
    @TypeLabel("android_score")
    @JsonClass(generateAdapter = false)
    data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    @delegate:Transient
    private val score: Float by unsafeLazy {
    var count = 0f
    if (hasAndroidAssets) count += 2
    if (hasAndroidRes) count += 2
    if (usesAndroidClasses) count += 2
    if (hasBuildConfig) count += 0.5f
    if (hasAndroidDependencies) count += 0.5f
    count
    }

    View Slide

  67. /** True if this project uses no Android facilities at all. */
    fun shouldBeJvm(): Boolean = score == 0f
    /** True if this project only uses some limited number of Android facilities. */
    fun couldBeJvm(): Boolean = score < THRESHOLD
    Android Library ➡ JVM Module Advice
    @TypeLabel("android_score")
    @JsonClass(generateAdapter = false)
    data class AndroidScore(
    val hasAndroidAssets: Boolean,
    val hasAndroidRes: Boolean,
    val usesAndroidClasses: Boolean,
    val hasBuildConfig: Boolean,
    val hasAndroidDependencies: Boolean,
    ) : ModuleAdvice() {
    @delegate:Transient
    private val score: Float by unsafeLazy {
    var count = 0f
    if (hasAndroidAssets) count += 2
    if (hasAndroidRes) count += 2
    if (usesAndroidClasses) count += 2
    if (hasBuildConfig) count += 0.5f
    if (hasAndroidDependencies) count += 0.5f
    count
    }

    View Slide

  68. Module Advice from ./gradlew buildHealth
    Advice for :samples:android_lib


    Module structure advice


    This project uses limited Android features and could be a JVM project.


    * Includes BuildConfig.


    View Slide

  69. Refactoring is Rarely Trivial

    View Slide

  70. Extract Android Specific Code
    • Android Classes
    • BuildCon
    fi
    g
    • Assets
    • Strings
    • Activities
    • Layouts
    • Drawables

    View Slide

  71. Kotlin JVM Kotlin Multiplatform
    Step 2

    View Slide

  72. KMP-Readiness
    github.com/handstandsam/kmp-readiness

    View Slide

  73. KMP-Readiness Decisioning
    1. Multiplatform Plugin Already Enabled?
    2. Only Kotlin Code?
    3. References to java.util, etc?
    4. Multiplatform Compatible Dependencies?

    View Slide

  74. Multiplatform Compatible
    Dependencies?

    View Slide

  75. View Slide

  76. View Slide

  77. View Slide

  78. 9 Days Later …

    View Slide

  79. View Slide

  80. Maven Search
    https://search.maven.org/search?q=ktor-client

    View Slide

  81. Maven Search JSON API
    https://search.maven.org/solrsearch/select?q=g%3Aorg.jetbrains.kotlinx+AND+a%3Akotlinx-datetime+AND+v%3A0.3.2+AND+p%3Ajar+AND+l%3Akotlin-tooling-metadata&rows=1&wt=json
    {
    "response": {
    "numFound": 1,
    "start": 0,
    "docs": [
    {
    "id": "org.jetbrains.kotlinx:kotlinx-datetime:0.3.2",
    "g": "org.jetbrains.kotlinx",
    "a": "kotlinx-datetime",
    "v": "0.3.2",
    "p": "jar",
    "timestamp": 1641926665000,
    "ec": [
    "-javadoc.jar",
    "-sources.jar",
    ".jar",
    "-all.jar",
    "-kotlin-tooling-metadata.json",
    ".module",
    ".pom"
    ],
    "tags": [
    "kotlin",
    "library",
    "datetime"
    ]
    }
    ]
    }
    }
    https://search.maven.org/search?q=g:org.jetbrains.kotlinx%20AND%20a:kotlinx-datetime%20AND%20v:0.3.2%20AND%20p:jar%20AND%20l:kotlin-tooling-metadata

    View Slide

  82. Maven Search JSON API
    https://search.maven.org/solrsearch/select?q=g%3Aorg.jetbrains.kotlinx+AND+a%3Akotlinx-datetime+AND+v%3A0.3.2+AND+p%3Ajar+AND+l%3Akotlin-tooling-metadata&rows=1&wt=json
    "-kotlin-tooling-metadata.json"

    View Slide

  83. kotlin-metadata-tooling.json

    View Slide

  84. {
    "schemaVersion": "1.0.0",
    "buildSystem": "Gradle",
    "buildSystemVersion": "7.5.1",
    "buildPlugin": "org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper",
    "buildPluginVersion": "1.6.21",
    "projectSettings": {
    "isHmppEnabled": true,
    "isCompatibilityMetadataVariantEnabled": true
    },
    "projectTargets": [
    {
    "target": "org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget",
    "platformType": "jvm",
    "extras": {
    "jvm": {
    "jvmTarget": "1.8",
    "withJavaEnabled": false
    }
    }
    },
    {
    "target": "org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget",
    "platformType": "common"
    }
    ]
    }
    kotlin-tooling-metadata.json
    https://repo1.maven.org/maven2/io/ktor/ktor-client/2.1.0/ktor-client-2.1.0-kotlin-tooling-metadata.json

    View Slide

  85. "platformType": "common"
    kotlin-tooling-metadata.json
    https://repo1.maven.org/maven2/io/ktor/ktor-client/2.1.0/ktor-client-2.1.0-kotlin-tooling-metadata.json

    View Slide

  86. See Created kotlin-tooling-metadata.json for the Kotlin Team

    View Slide

  87. February 2nd, 2021 - Kotlin Lang Slack

    View Slide

  88. kdoctor
    https://github.com/Kotlin/kdoctor

    View Slide

  89. KMP-readiness
    github.com/handstandsam/kmp-readiness
    Demo

    View Slide


  90. View Slide

  91. Kotlin Multiplatform
    Multiplatformization Success!

    View Slide

  92. What’s the Cost?

    View Slide

  93. Upfront Investment, Low Risk, …

    View Slide

  94. Run Anywhere*
    Write Once
    * Yes, but…

    View Slide

  95. Coming Soon!
    Kotlin Multiplatform Beta

    View Slide

  96. Minimize Risk during Multiplatformization
    We Can

    View Slide

  97. Test and Experiment ♻
    • Create a multiplatform iOS and Desktop app that exercises it

    • Create Shadow Jobs to Build Multiplatform to ensure compatibility

    View Slide

  98. 2023 is the Year of
    Multiplatformization

    View Slide

  99. Is a helpful tool on your path to Multiplaformization in 2023
    KMP4FREE

    View Slide

  100. Is a helpful tool on your path to Multiplaformization in 2023
    KMP-Readiness

    View Slide

  101. With Multiplatform in Mind
    Write Code

    View Slide

  102. Thank You

    View Slide