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

Hi, have you met Kotlin Multiplatform? | Marca ...

Hi, have you met Kotlin Multiplatform? | Marca User Group

Kotlin Multiplatform is getting more and more hype every day, even if it’s still in alpha. We constantly read of new companies and teams that are trying KMP for experiments and production projects alike. And we’re left wondering: it safe to do so? Why picking KMP over any another 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 to these questions, clarifying all the doubts and making you ready to use and love Kotlin Multiplatform.

Marco Gomiero

November 26, 2020
Tweet

More Decks by Marco Gomiero

Other Decks in Programming

Transcript

  1. Marca User Group - @marcoGomier Marco Gomiero Tech Lead @

    Uniwhere Co-Lead @ GDG Venezia > Twitter: @marcoGomier > Github: prof18 > Website: marcogomiero.com
  2. Marca User Group - @marcoGomier • Cons: use of “middlemen”,

    custom widgets • Unifying UI declaration between platform is complicated • Different platforms have different patterns “Classic” Cross Platform Solutions
  3. Marca User Group - @marcoGomier • Not about compiling all

    code for all platforms • Share as much [NO UI] code as possible Kotlin Multiplatform
  4. Marca User Group - @marcoGomier Common Kotlin Kotlin/JVM Kotlin/JS Kotlin/Native

    Java Android Browser NodeJS Android NDK iOs macOS watchOS tvOS Linux Windows
  5. Marca User Group - @marcoGomier Common Kotlin Kotlin/JVM Kotlin/JS Kotlin/Native

    Java Android Browser NodeJS Android NDK iOs macOS watchOS tvOS Linux Windows Mobile App
  6. Marca User Group - @marcoGomier Common Kotlin Kotlin/JVM Kotlin/JS Kotlin/Native

    Java Android Browser NodeJS Android NDK iOs macOS watchOS tvOS Linux Windows Mobile & Desktop App + JVM Backend
  7. Marca User Group - @marcoGomier Common Kotlin Kotlin/JVM Kotlin/JS Kotlin/Native

    Java Android Browser NodeJS Android NDK iOs macOS watchOS tvOS Linux Windows Full Stack JS App
  8. Marca User Group - @marcoGomier • expect/actual mechanism • Declaration

    in common target and implementation in all specific targets • Works for functions, classes, interfaces, enumerations, properties, and annotations. Platform-specific APIs?
  9. Marca User Group - @marcoGomier Android expect fun debugLog(tag: String,

    message: String) import android.util.Log actual fun debugLog(tag: String, message: String) { Log.d(tag, message) }
  10. Marca User Group - @marcoGomier iOs expect fun debugLog(tag: String,

    message: String) import platform.Foundation.NSLog actual fun debugLog(tag: String, message: String) { if (Platform.isDebugBinary) { NSLog("%s: %s", tag, message) } }
  11. Marca User Group - @marcoGomier Common Kotlin Android App iOs

    App .aar Framework Greenfield Project Same Repository
  12. Marca User Group - @marcoGomier iOs: packForXcode val packForXcode by

    tasks.creating(Sync"::class) { group = "build" val mode = System.getenv("CONFIGURATION") "?: "DEBUG" val sdkName = System.getenv("SDK_NAME") "?: "iphonesimulator" val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64" val framework = kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode) inputs.property("mode", mode) dependsOn(framework.linkTask) val targetDir = File(buildDir, "xcode-frameworks") from({ framework.outputDirectory }) into(targetDir) } tasks.getByName("build").dependsOn(packForXcode) Auto generated when creating a Kotlin Multiplatform mobile app
  13. Marca User Group - @marcoGomier Pod"::Spec.new do |spec| spec.name =

    'shared' spec.version = '1.0-SNAPSHOT' spec.homepage = 'Link to a Kotlin/Native module homepage' spec.source = { :git "=> "Not Published", :tag "=> "Cocoapods/"#{spec.name}/"#{spec.version}" } spec.authors = '' spec.license = '' spec.summary = 'Some description for a Kotlin/Native module' spec.static_framework = true spec.vendored_frameworks = "build/cocoapods/framework/shared.framework" spec.libraries = "c"++" spec.module_name = ""#{spec.name}_umbrella" spec.pod_target_xcconfig = { 'KOTLIN_TARGET[sdk=iphonesimulator*]' "=> 'ios_x64', 'KOTLIN_TARGET[sdk=iphoneos*]' "=> 'ios_arm', 'KOTLIN_TARGET[sdk=watchsimulator*]' "=> 'watchos_x86', 'KOTLIN_TARGET[sdk=watchos*]' "=> 'watchos_arm', 'KOTLIN_TARGET[sdk=appletvsimulator*]' "=> 'tvos_x64', 'KOTLIN_TARGET[sdk=appletvos*]' "=> 'tvos_arm64', 'KOTLIN_TARGET[sdk=macosx*]' "=> 'macos_x64' } spec.script_phases = [ { :name "=> 'Build shared', :execution_position "=> :before_compile, :shell_path "=> '/bin/sh', :script "=> ""<<-SCRIPT set -ev REPO_ROOT="$PODS_TARGET_SRCROOT" "$REPO_ROOT/"../gradlew" -p "$REPO_ROOT" :shared:syncFramework \ -Pkotlin.native.cocoapods.target=$KOTLIN_TARGET \ -Pkotlin.native.cocoapods.configuration=$CONFIGURATION \ -Pkotlin.native.cocoapods.cflags="$OTHER_CFLAGS" \ -Pkotlin.native.cocoapods.paths.headers="$HEADER_SEARCH_PATHS" \ -Pkotlin.native.cocoapods.paths.frameworks="$FRAMEWORK_SEARCH_PATHS" SCRIPT } ] end CocoaPods plugin: shared.podspec https://kotlinlang.org/docs/reference/native/cocoapods.html
  14. Marca User Group - @marcoGomier } spec.script_phases = [ {

    :name "=> 'Build shared', :execution_position "=> :before_compile, :shell_path "=> '/bin/sh', :script "=> ""<<-SCRIPT set -ev REPO_ROOT="$PODS_TARGET_SRCROOT" "$REPO_ROOT/"../gradlew" -p "$REPO_ROOT" :shared:syncFramework \ -Pkotlin.native.cocoapods.target=$KOTLIN_TARGET \ -Pkotlin.native.cocoapods.configuration=$CONFIGURATION \ -Pkotlin.native.cocoapods.cflags="$OTHER_CFLAGS" \ -Pkotlin.native.cocoapods.paths.headers="$HEADER_SEARCH_PATHS" \ -Pkotlin.native.cocoapods.paths.frameworks="$FRAMEWORK_SEARCH_PATHS" SCRIPT } ] end https://kotlinlang.org/docs/reference/native/cocoapods.html
  15. Marca User Group - @marcoGomier Common Kotlin Android App iOs

    App .aar Framework Brownfield Project Android App Repository KMP Repository iOs App Repository
  16. Marca User Group - @marcoGomier iOs Project: Podfile pod 'HNFoundation',

    :git "=> "[email protected]:prof18/hn-foundation-cocoa.git", :tag "=> '1.0.0'
  17. Marca User Group - @marcoGomier plugins { "//""... id("maven-publish") }

    group = "com.prof18.hn.foundation" version = "1.0" publishing { repositories { maven{ credentials { username = "username" password = "pwd" } url = url("https:"//mymavenrepo.it") } } } How to publish Android: Maven repository
  18. Marca User Group - @marcoGomier packForXcode Auto generated when creating

    a Kotlin Multiplatform mobile app val packForXcode by tasks.creating(Sync"::class) { group = "build" val mode = System.getenv("CONFIGURATION") "?: "DEBUG" val sdkName = System.getenv("SDK_NAME") "?: "iphonesimulator" val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64" val framework = kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode) inputs.property("mode", mode) dependsOn(framework.linkTask) val targetDir = File(buildDir, "xcode-frameworks") from({ framework.outputDirectory }) into(targetDir) } tasks.getByName("build").dependsOn(packForXcode)
  19. Marca User Group - @marcoGomier spec.pod_target_xcconfig = { 'KOTLIN_TARGET[sdk=iphonesimulator*]' "=>

    'ios_x64', 'KOTLIN_TARGET[sdk=iphoneos*]' "=> 'ios_arm', 'KOTLIN_TARGET[sdk=watchsimulator*]' "=> 'watchos_x86', 'KOTLIN_TARGET[sdk=watchos*]' "=> 'watchos_arm', 'KOTLIN_TARGET[sdk=appletvsimulator*]' "=> 'tvos_x64', 'KOTLIN_TARGET[sdk=appletvos*]' "=> 'tvos_arm64', 'KOTLIN_TARGET[sdk=macosx*]' "=> 'macos_x64' } spec.script_phases = [ { :name "=> 'Build shared', :execution_position "=> :before_compile, :shell_path "=> '/bin/sh', :script "=> ""<<-SCRIPT set -ev REPO_ROOT="$PODS_TARGET_SRCROOT" "$REPO_ROOT/"../gradlew" -p "$REPO_ROOT" :shared:syncFramework \ -Pkotlin.native.cocoapods.target=$KOTLIN_TARGET \ -Pkotlin.native.cocoapods.configuration=$CONFIGURATION \ -Pkotlin.native.cocoapods.cflags="$OTHER_CFLAGS" \ -Pkotlin.native.cocoapods.paths.headers="$HEADER_SEARCH_PATHS" \ -Pkotlin.native.cocoapods.paths.frameworks="$FRAMEWORK_SEARCH_PATHS" SCRIPT } ] CocoaPods plugin: shared.podspec https://kotlinlang.org/docs/reference/native/cocoapods.html
  20. Marca User Group - @marcoGomier val libName = "HNFoundation" tasks

    { register("universalFrameworkDebug", org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask"::class) { baseName = libName from( iosArm64().binaries.getFramework(libName, "Debug"), iosX64().binaries.getFramework(libName, "Debug") ) destinationDir = buildDir.resolve("$rootDir/"../"../hn-foundation-cocoa") group = libName description = "Create the debug framework for iOs" dependsOn("linkHNFoundationDebugFrameworkIosArm64") dependsOn("linkHNFoundationDebugFrameworkIosX64") } ""... } Custom Gradle Task: universalFrameworkDebug https://github.com/prof18/shared-hn-android-ios-backend/blob/master/hn-foundation/build.gradle.kts#L100
  21. Marca User Group - @marcoGomier Custom Gradle Task: universalFrameworkDebug val

    libName = "HNFoundation" tasks { register("universalFrameworkDebug", org.jetbrains.kotlin.gradle.tasks.FatFrameworkTask"::class) { baseName = libName from( iosArm64().binaries.getFramework(libName, "Debug"), iosX64().binaries.getFramework(libName, "Debug") ) destinationDir = buildDir.resolve("$rootDir/"../"../hn-foundation-cocoa") group = libName description = "Create the debug framework for iOs" dependsOn("linkHNFoundationDebugFrameworkIosArm64") dependsOn("linkHNFoundationDebugFrameworkIosX64") } ""... } https://github.com/prof18/shared-hn-android-ios-backend/blob/master/hn-foundation/build.gradle.kts#L100
  22. Automatic publishing task register("publishFramework") { description = "Publish iOs framework

    to the Cocoa Repo" project.exec { workingDir = File("$rootDir/"../"../hn-foundation-cocoa") commandLine("git", "checkout", "master").standardOutput } "// Create Release Framework for Xcode dependsOn("universalFrameworkRelease") "// Replace doLast { val dir = File("$rootDir/"../"../hn-foundation-cocoa/HNFoundation.podspec") val tempFile = File("$rootDir/"../"../hn-foundation-cocoa/HNFoundation.podspec.new") val reader = dir.bufferedReader() val writer = tempFile.bufferedWriter() var currentLine: String?
  23. Marca User Group - @marcoGomier Marca User Group- @marcoGomier .

    └── kmm-project ├── androidApp ├── iosApp └── shared Photo by Ashkan Forouzani on Unsplash
  24. Marca User Group - @marcoGomier You don’t have enough time

    for a big rewrite Photo by Ashkan Forouzani on Unsplash
  25. Marca User Group - @marcoGomier . └── kmm-project ├── androidApp

    ├── iosApp └── shared Photo by Ashkan Forouzani on Unsplash Marca User Group- @marcoGomier
  26. Marca User Group - @marcoGomier Photo by Erwan Hesry on

    Unsplash You can choose a little piece of tech stack to start with
  27. Marca User Group - @marcoGomier • Boring code to write

    multiple times • Code/feature that centralises the source of truth (i.e. a field is nullable or not) • Code/feature that can be gradually extracted Where to start?
  28. Marca User Group - @marcoGomier • DTOs • Common Models

    • Utility methods, aka `object Utils {}` Where to start?
  29. Marca User Group - @marcoGomier Start little then go bigger

    • Validated the process with “little” effort • Next, go bigger and share more “features” For example the data layer, the network layer, etc
  30. Marca User Group - @marcoGomier • Kotlin/Native different from the

    JVM • One thread "-> no problems, you can use mutable objects • Many thread "-> only immutable objects Concurrency https://kotlinlang.org/docs/mobile/concurrency-overview.html
  31. Marca User Group - @marcoGomier • 1.4.1 "-> single thread

    on K/N • 1.4.1-native-mt "-> multithreaded version Coroutines https://kotlinlang.org/docs/mobile/concurrency-and-coroutines.html
  32. Marca User Group - @marcoGomier • Networking: Ktor • Persistence:

    SQLDelight, multiplatform-settings • Serialization: kotlinx.serialization • Dependency Injection: koin, Kodein-DI • Reactive: Reaktive • Date/Time: kotlinx-datetime (still experimental) Common Libraries
  33. Marca User Group - @marcoGomier • Kotlin Multiplatform "!= Cross

    Platform • Early adoption stage • It’s the future Conclusions
  34. Marco Gomiero Marca User Group Thank you! > Twitter: @marcoGomier

    > Github: prof18 > Website: marcogomiero.com