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

Hi, have you met Kotlin Multiplatform | DevFest Hamburg

Hi, have you met Kotlin Multiplatform | DevFest Hamburg

Kotlin Multiplatform is getting more and more hype every day. It started as an experimental technology, then alpha, beta and now it's on the path of becoming stable.

We constantly read of new companies and teams that are using KMP for experiments and production projects alike. And we're left wondering: why pick KMP over any other cross-platform solution? How to approach it? And, most importantly, is it possible to start using it in existing projects?

In this talk, I'll answer these questions, clarifying all the doubts and making you ready to use and love Kotlin Multiplatform.

Marco Gomiero

November 03, 2023
Tweet

More Decks by Marco Gomiero

Other Decks in Programming

Transcript

  1. 👨💻 Senior Android Engineer

    Google Developer Expert for Kotlin
    Marco Gomiero
    @marcoGomier
    Hi, have you met


    Kotlin
    Multiplatform?

    View full-size slide

  2. @marcoGomier
    Kotlin Multiplatform is now stable! 🥳
    https://blog.jetbrains.com/kotlin/2023/11/kotlin-multiplatform-stable/

    View full-size slide

  3. @marcoGomier
    “Classic” Cross Platform Solutions
    • All-in approach


    • Everything is shared, UI included


    • Different platforms have different patterns

    View full-size slide

  4. @marcoGomier
    Kotlin Multiplatform
    • Incremental approach


    • You choose what to share (even UI, if you want)

    View full-size slide

  5. @marcoGomier
    Common Kotlin
    Kotlin/JVM Kotlin/JS
    Kotlin/Native
    JVM


    Android
    Browser


    NodeJS
    iOS


    macOS


    watchOS


    tvOS


    Linux


    Windows
    Supported Platforms

    View full-size slide

  6. @marcoGomier
    Common Kotlin
    Kotlin/JVM Kotlin/JS
    Kotlin/Native
    JVM


    Android
    Browser


    NodeJS
    iOS


    macOS


    watchOS


    tvOS


    Linux


    Windows
    Mobile

    View full-size slide

  7. @marcoGomier
    Beyond Mobile
    https://hamburg.devfest.de/sessions/108

    View full-size slide

  8. @marcoGomier
    Common and Platform-specific code
    • Write regular Kotlin code in the
    common source set


    • Platform-specific code goes into
    platform source sets

    View full-size slide

  9. @marcoGomier
    Platform-specific code
    • Expect/Actual


    View full-size slide

  10. @marcoGomier
    Expect/Actual
    https://kotlinlang.org/docs/multiplatform-connect-to-apis.html

    View full-size slide

  11. @marcoGomier
    Expect/Actual
    expect fun debugLog(tag: String, message: String)

    View full-size slide

  12. @marcoGomier
    expect fun debugLog(tag: String, message: String)
    import android.util.Log


    actual fun debugLog(tag: String, message: String) {


    Log.d(tag, message)


    }
    import platform.Foundation.NSLog


    actual fun debugLog(tag: String, message: String) {


    if (Platform.isDebugBinary) {


    NSLog("%s: %s", tag, message)


    }


    }

    View full-size slide

  13. @marcoGomier
    Platform-specific code
    • Expect/Actual


    • Interfaces


    View full-size slide

  14. @marcoGomier
    internal interface XmlFetcher {


    suspend fun fetchXml(url: String): ParserInput


    }
    Interface

    View full-size slide

  15. @marcoGomier
    internal interface XmlFetcher {


    suspend fun fetchXml(url: String): ParserInput


    }
    internal class JvmXmlFetcher(


    private val callFactory: Call.Factory,


    ): XmlFetcher {


    override suspend fun fetchXml(url: String): ParserInput {


    val request = createRequest(url)


    return ParserInput(


    inputStream = callFactory.newCall(request).await()


    )


    }


    }
    internal class IosXmlFetcher(


    private val nsUrlSession: NSURLSession,


    ): XmlFetcher {


    override suspend fun fetchXml(url: String): ParserInput =


    suspendCancellableCoroutine { continuation ->


    ...


    }


    }

    View full-size slide

  16. @marcoGomier
    import org.jsoup.Jsoup


    internal class JvmHtmlParser : HtmlParser {


    override fun getTextFromHTML(html: String): String? {


    return try {


    val doc = Jsoup.parse(html)


    doc.text()


    } catch (e: Throwable) {


    null


    }


    }


    }
    import shared


    import SwiftSoup


    class IosHtmlParser: HtmlParser {


    func getTextFromHTML(html: String) -> String? {


    do {


    let doc: Document = try SwiftSoup.parse(html)


    return try doc.text()


    } catch {


    return nil


    }


    }


    }
    interface HtmlParser {


    fun getTextFromHTML(html: String): String?


    }

    View full-size slide

  17. @marcoGomier
    Platform-specific code
    • Expect/Actual


    • Interfaces


    • Prefer interfaces, if possible

    View full-size slide

  18. @marcoGomier
    How it works?

    View full-size slide

  19. @marcoGomier
    shared
    androidApp
    .aar
    Android

    View full-size slide

  20. @marcoGomier
    shared
    androidApp iosApp
    .aar Framework
    iOS
    Android

    View full-size slide

  21. @marcoGomier
    XCFramework

    View full-size slide

  22. @marcoGomier
    NEW
    https://devstreaming-cdn.apple.com/videos/wwdc/2019/416h8485aty341c2/416/416_binary_frameworks_in_swift.pdf

    View full-size slide

  23. @marcoGomier https://devstreaming-cdn.apple.com/videos/wwdc/2019/416h8485aty341c2/416/416_binary_frameworks_in_swift.pdf
    NEW
    iOS mac
    OS
    watch
    OS
    tvOS

    View full-size slide

  24. @marcoGomier
    Kotlin
    ->
    Obj-c
    ->
    Swift

    View full-size slide

  25. @marcoGomier
    when (appState.newsState) {


    is NewsState.Loading
    ->
    {


    progressBar.visibility = View.VISIBLE


    ...

    }


    is NewsState.Error
    ->
    {


    errorButton.visibility = View.VISIBLE


    errorMessage.visibility = View.VISIBLE


    ...

    }


    is NewsState.Success
    ->
    {


    progressBar.visibility = View.GONE


    errorMessage.visibility = View.GONE


    recyclerView.visibility = View.VISIBLE


    ...

    }


    }


    View full-size slide

  26. @marcoGomier
    Some gotchas
    • No namespaces


    • No default parameters


    • Enums are not Swift-friendly (no values)


    • Sealed classes are simple classes


    • Coroutines and Flows

    View full-size slide

  27. @marcoGomier https://github.com/rickclephas/KMP-NativeCoroutines

    View full-size slide

  28. @marcoGomier https://github.com/touchlab/SKIE

    View full-size slide

  29. @marcoGomier
    Make it a team effort

    View full-size slide

  30. @marcoGomier
    New projects

    View full-size slide

  31. @marcoGomier
    Create a new app project

    View full-size slide

  32. @marcoGomier

    View full-size slide

  33. @marcoGomier

    View full-size slide

  34. @marcoGomier

    View full-size slide

  35. @marcoGomier
    shared
    androidApp
    Gradle Module
    Android

    View full-size slide

  36. @marcoGomier
    // settings.gradle.kts


    include(":shared")


    // build.gradle.kts


    implementation(project(":shared"))
    Android

    View full-size slide

  37. @marcoGomier
    shared
    androidApp iosApp
    Gradle Module Framework
    iOS
    Android

    View full-size slide

  38. @marcoGomier
    Create a new app project

    View full-size slide

  39. @marcoGomier
    Regular Framework

    View full-size slide

  40. @marcoGomier
    Create a new app project

    View full-size slide

  41. @marcoGomier
    CocoaPods integration
    https://kotlinlang.org/docs/reference/native/cocoapods.html
    Pod::Spec.new do |spec|


    spec.name = 'shared'


    spec.version = '1.0'


    spec.homepage = 'Link to the Shared Module homepage'


    spec.source = { :http=> ''}


    spec.authors = ''


    spec.license = ''


    spec.summary = 'Some description for the Shared Module'


    spec.vendored_frameworks = 'build/cocoapods/framework/shared.framework'


    spec.libraries = 'c++'


    spec.ios.deployment_target = '14.1'






    spec.pod_target_xcconfig = {


    'KOTLIN_PROJECT_PATH' => ':shared',


    'PRODUCT_MODULE_NAME' => 'shared',


    }



    spec.script_phases = [


    {


    :name => 'Build shared',


    :execution_position => :before_compile,


    :shell_path => '/bin/sh',


    :script => <<-SCRIPT


    if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then


    echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment


    exit 0


    fi


    set -ev


    REPO_ROOT="$PODS_TARGET_SRCROOT"


    "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \


    -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \


    -Pkotlin.native.cocoapods.archs="$ARCHS" \


    -Pkotlin.native.cocoapods.configuration="$CONFIGURATION"


    SCRIPT


    }


    ]



    end


    View full-size slide

  42. @marcoGomier
    iOSApp: Podfile
    https://kotlinlang.org/docs/reference/native/cocoapods.html
    target 'iosApp' do


    use_frameworks!


    platform :ios, '14.1'


    pod 'shared', :path => '../shared'


    end

    View full-size slide

  43. @marcoGomier
    shared
    androidApp iosApp
    Gradle Module Framework
    iOS
    Android

    View full-size slide

  44. @marcoGomier
    shared
    androidApp iosApp
    Framework
    Same Repository
    Gradle
    Module

    View full-size slide

  45. @marcoGomier
    Existing projects

    View full-size slide

  46. @marcoGomier
    🙅
    shared
    androidApp iosApp
    Gradle
    Module
    Framework
    Same Repository
    Existing projects

    View full-size slide

  47. @marcoGomier
    💡 Create a library!

    View full-size slide

  48. @marcoGomier
    Create a new library project

    View full-size slide

  49. @marcoGomier

    View full-size slide

  50. @marcoGomier
    Common Kotlin
    Android App iOS App
    .aar XCFramework

    View full-size slide

  51. @marcoGomier
    Common Kotlin
    Android App iOS App
    .aar XCFramework

    Android App Repository
    KMP Repository
    iOs App Repository

    View full-size slide

  52. @marcoGomier
    How to publish: Android

    View full-size slide

  53. @marcoGomier
    Setup a Maven repository to share the artifacts: build.gradle.kts
    plugins {


    //...


    id("maven-publish")


    }


    group = "com.prof18.hn.foundation"


    version = "1.0"


    publishing {


    repositories {


    maven {


    credentials {


    username = "username"


    password = "pwd"


    }


    url = URI.create("https://mymavenrepo.it")


    }


    }


    }

    View full-size slide

  54. @marcoGomier
    Publish the artifacts
    ./gradlew publish


    View full-size slide

  55. @marcoGomier
    Publish the artifacts
    ./gradlew publish


    ./gradlew publishToMavenLocal


    View full-size slide

  56. @marcoGomier
    How to publish: iOS

    View full-size slide

  57. @marcoGomier
    🥵

    View full-size slide

  58. @marcoGomier
    XCFramework

    View full-size slide

  59. @marcoGomier
    Build an XCFramework
    https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#build-xcframeworks
    import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework


    val libName = "LibraryName"


    kotlin {


    val xcFramework = XCFramework(libName)


    ios {


    binaries.framework(libName) {


    xcFramework.add(this)


    }


    }


    ...


    }

    View full-size slide

  60. @marcoGomier
    XCFramework: Gradle tasks
    assemble${libName}XCFramework


    assemble${libName}DebugXCFramework


    assemble${libName}ReleaseXCFramework

    View full-size slide

  61. @marcoGomier
    Build an XCFramework

    View full-size slide

  62. @marcoGomier https://github.com/touchlab/KMMBridge

    View full-size slide

  63. @marcoGomier https://github.com/prof18/kmp-framework-bundler

    View full-size slide

  64. @marcoGomier
    Where to start

    View full-size slide

  65. @marcoGomier
    shared
    androidApp iosApp
    Gradle
    Module
    Framework
    Same Repository
    New projects

    View full-size slide

  66. @marcoGomier
    🙅
    shared
    androidApp iosApp
    Gradle
    Module
    Framework
    Same Repository
    Existing projects

    View full-size slide

  67. @marcoGomier
    💡 Create a library!

    View full-size slide

  68. @marcoGomier
    • Boring code to write multiple times


    • Code/feature that centralizes the source of truth


    • Code/feature that can be gradually extracted
    Where to start?

    View full-size slide

  69. @marcoGomier
    • DTOs


    • Common Models


    • Utility methods


    • Analytics



    . . .
    Where to start?

    View full-size slide

  70. @marcoGomier
    Start little

    View full-size slide

  71. @marcoGomier
    Start little
    Go bigger
    then

    View full-size slide

  72. @marcoGomier
    Start little then go bigger
    • Validate the process with “little” effort


    • Then you can go bigger and share more “features”

    View full-size slide

  73. @marcoGomier
    Ecosystem

    View full-size slide

  74. @marcoGomier
    • Networking: Ktor, Apollo


    • Persistence: SQLDelight, multiplatform-settings, Realm


    • Serialization: kotlinx.serialization


    • Dependency Injection: Koin, Kodein, kotlin-inject


    • Asynchronous: Coroutines, Reaktive


    • Date/Time: kotlinx-datetime


    • Logging: Kermit, Napier
    Common Libraries
    And much more
    ...

    View full-size slide

  75. @marcoGomier
    https://github.com/AAkira/Kotlin-Multiplatform-Libraries
    https://github.com/terrakok/kmp-awesome

    View full-size slide

  76. @marcoGomier https://github.com/touchlab/xcode-kotlin

    View full-size slide

  77. @marcoGomier https://github.com/touchlab/xcode-kotlin

    View full-size slide

  78. @marcoGomier https://github.com/touchlab/xcode-kotlin

    View full-size slide

  79. @marcoGomier https://github.com/touchlab/CrashKiOS

    View full-size slide

  80. @marcoGomier https://github.com/touchlab/CrashKiOS

    View full-size slide

  81. @marcoGomier
    Conclusions

    View full-size slide

  82. @marcoGomier
    • Kotlin Multiplatform
    ! =
    Cross Platform


    • Stable since Kotlin 1.9.20


    Conclusions

    View full-size slide

  83. @marcoGomier https://www.droidcon.com/2023/07/31/10-myths-about-crossplatform-mobile-development-with-kotlin/

    View full-size slide

  84. @marcoGomier
    • Kotlin Multiplatform
    ! =
    Cross Platform


    • Stable since Kotlin 1.9.20


    • It’s the future


    • It’s a joint and team approach
    Conclusions

    View full-size slide

  85. Bibliography / Useful Links
    • https:
    //
    kotlinlang.org/docs/multiplatform.html


    • https:
    //
    kotlinlang.org/docs/native-overview.html


    • https:
    //
    kotlinlang.org/docs/js-overview.html


    • https:
    //
    kotlinlang.org/docs/multiplatform-share-on-platforms.html


    • https:
    //
    kotlinlang.org/docs/multiplatform-connect-to-apis.html


    • https:
    //
    devstreaming-cdn.apple.com/videos/wwdc/2019/416h8485aty341c2/416/416_binary_frameworks_in_swift.pdf


    • https:
    //
    developer.apple.com/videos/play/wwdc2019/416/


    • https:
    //
    medium.com/@aoriani/list/writing-swiftfriendly-kotlin-multiplatform-apis-c51c2b317fce


    • https:
    //
    kotlinlang.org/docs/native-objc-interop.html


    • https:
    //
    speakerdeck.com/kpgalligan/kotlinconf-2023-kotlin-mobile-multiplatform-for-teams


    • https:
    //
    speakerdeck.com/kpgalligan/sdk-design-and-publishing-for-kotlin-multiplatform-mobile


    • https:
    //
    kotlinlang.org/docs/reference/native/cocoapods.html


    • https:
    //
    kotlinlang.org/docs/multiplatform-build-native-binaries.html#build-xcframeworks


    • https:
    //
    github.com/touchlab/KMMBridge


    • https:
    //
    github.com/prof18/kmp-framework-bundler


    • https:
    //
    github.com/touchlab/xcode-kotlin


    • https:
    //
    github.com/terrakok/kmp-awesome


    • https:
    //
    github.com/AAkira/Kotlin-Multiplatform-Libraries


    • https:
    //
    github.com/touchlab/CrashKiOS


    • https:
    //
    www.droidcon.com/2023/07/31/10-myths-about-crossplatform-mobile-development-with-kotlin/


    • https:
    //
    blog.jetbrains.com/kotlin/2023/11/kotlin-multiplatform-stable/

    View full-size slide

  86. @marcoGomier
    Thank you!
    > Twitter: @marcoGomier

    > Github: prof18

    > Website: marcogomiero.com

    > Mastodon: androiddev.social/@marcogom
    👨💻 Senior Android Engineer

    Google Developer Expert for Kotlin
    Marco Gomiero

    View full-size slide