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

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

    Multiplaformization in 2023 Droidcon NYC 2022
  2. 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
  3. JVM Project Structure plugins { kotlin("jvm") } dependencies { implementation(libs.kotlin.stdlib)

    implementation(project(":samples:jvm_kmp4free")) testImplementation(libs.kotlin.test.common) testImplementation(libs.truth) }
  4. 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) }
  5. 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
  6. 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
  7. “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.”
  8. 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
  9. Plant a Seed 🌱 • Same Kotlin Code • Di

    ff erent Gradle plugin • Forces modularity • Makes developers more likely to try coding in the other platform
  10. data class AndroidScore( val hasAndroidAssets: Boolean, val hasAndroidRes: Boolean, val

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

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

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

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

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

    usesAndroidClasses: Boolean, val hasBuildConfig: Boolean, val hasAndroidDependencies: Boolean, ) : ModuleAdvice() { Android Library ➡ JVM Module Advice
  16. @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
  17. @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
  18. /** 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 }
  19. /** 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 }
  20. 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.
  21. Extract Android Specific Code • Android Classes • BuildCon fi

    g • Assets • Strings • Activities • Layouts • Drawables
  22. KMP-Readiness Decisioning 1. Multiplatform Plugin Already Enabled? 2. Only Kotlin

    Code? 3. References to java.util, etc? 4. Multiplatform Compatible Dependencies?
  23. 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
  24. { "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
  25. Test and Experiment ♻ • Create a multiplatform iOS and

    Desktop app that exercises it • Create Shadow Jobs to Build Multiplatform to ensure compatibility