Slide 1

Slide 1 text

Marco Gomiero @marcoGomier 👨💻 Senior Android Developer @ Airalo Google Developer Expert for Kotlin Hi, have you met Kotlin Multiplatform?

Slide 2

Slide 2 text

@marcoGomier https://www.youtube.com/watch?v=ddcZnW1HKUY

Slide 3

Slide 3 text

@marcoGomier “Classic” Cross Platform Solutions • All-in approach • Everything is shared, UI included • Different platforms have different patterns

Slide 4

Slide 4 text

@marcoGomier Kotlin Multiplatform • Incremental approach • You choose what to share (even UI, if you want)

Slide 5

Slide 5 text

@marcoGomier Common Kotlin Kotlin/JVM Kotlin/JS Kotlin/Native JVM Android Browser NodeJS iOS macOS watchOS tvOS Linux Windows Supported Platforms Kotlin/Wasm Browser Alpha

Slide 6

Slide 6 text

@marcoGomier Common Kotlin Kotlin/JVM Kotlin/JS Kotlin/Native JVM Android Browser NodeJS iOS macOS watchOS tvOS Linux Windows Mobile Kotlin/Wasm Browser Alpha

Slide 7

Slide 7 text

@marcoGomier Common and Platform-specific code • Write regular Kotlin code in the common source set • Platform-specific code goes into platform source sets

Slide 8

Slide 8 text

@marcoGomier Platform-specific code • Expect/Actual

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

@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) } }

Slide 12

Slide 12 text

@marcoGomier Platform-specific code • Expect/Actual • Interfaces

Slide 13

Slide 13 text

@marcoGomier internal interface XmlFetcher { suspend fun fetchXml(url: String): ParserInput } Interface

Slide 14

Slide 14 text

@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 -> ... } }

Slide 15

Slide 15 text

@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? }

Slide 16

Slide 16 text

@marcoGomier Platform-specific code • Expect/Actual • Interfaces • Prefer interfaces, if possible

Slide 17

Slide 17 text

@marcoGomier How it works?

Slide 18

Slide 18 text

@marcoGomier shared androidApp iosApp Library Library iOS Android

Slide 19

Slide 19 text

@marcoGomier shared androidApp Library Android

Slide 20

Slide 20 text

@marcoGomier shared androidApp .aar/Gradle module Android Just “regular” Kotlin code, like yet another library

Slide 21

Slide 21 text

@marcoGomier shared androidApp iosApp .aar/Gradle Module Library iOS Android

Slide 22

Slide 22 text

@marcoGomier shared iosApp Library iOS

Slide 23

Slide 23 text

@marcoGomier shared iosApp Framework iOS

Slide 24

Slide 24 text

@marcoGomier XCFramework https://developer.apple.com/documentation/xcode/creating-a-multi-platform-binary-framework-bundle

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

@marcoGomier shared iosApp Framework iOS Kotlin Objective-C Swift -> ->

Slide 28

Slide 28 text

@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 ... } }

Slide 29

Slide 29 text

@marcoGomier Some gotchas • No namespaces • No default parameters • Enums are not Swift-friendly (no values) • Sealed classes are simple classes • Coroutines without cancellation • Flows

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

@marcoGomier Make it a team effort

Slide 33

Slide 33 text

@marcoGomier New projects

Slide 34

Slide 34 text

@marcoGomier Create a new app project

Slide 35

Slide 35 text

@marcoGomier

Slide 36

Slide 36 text

@marcoGomier

Slide 37

Slide 37 text

@marcoGomier

Slide 38

Slide 38 text

@marcoGomier shared androidApp Gradle Module Android

Slide 39

Slide 39 text

@marcoGomier // settings.gradle.kts include(":shared") // build.gradle.kts implementation(project(":shared")) Android

Slide 40

Slide 40 text

@marcoGomier shared androidApp iosApp Gradle Module Framework iOS Android

Slide 41

Slide 41 text

@marcoGomier Create a new app project

Slide 42

Slide 42 text

@marcoGomier Regular Framework

Slide 43

Slide 43 text

@marcoGomier Create a new app project

Slide 44

Slide 44 text

@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

Slide 45

Slide 45 text

@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

Slide 46

Slide 46 text

@marcoGomier shared androidApp iosApp Gradle Module Framework iOS Android

Slide 47

Slide 47 text

@marcoGomier shared androidApp iosApp Framework Same Repository Gradle Module

Slide 48

Slide 48 text

@marcoGomier https://kmp.jetbrains.com/#newProject

Slide 49

Slide 49 text

@marcoGomier Existing projects

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

@marcoGomier 💡 Create a library!

Slide 52

Slide 52 text

@marcoGomier Create a new library project

Slide 53

Slide 53 text

@marcoGomier

Slide 54

Slide 54 text

@marcoGomier Common Kotlin Android App iOS App .aar XCFramework

Slide 55

Slide 55 text

@marcoGomier Common Kotlin Android App iOS App .aar XCFramework Android App Repository KMP Repository iOs App Repository

Slide 56

Slide 56 text

@marcoGomier https://terrakok.github.io/kmp-web-wizard/

Slide 57

Slide 57 text

@marcoGomier How to publish: Android

Slide 58

Slide 58 text

@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") } } }

Slide 59

Slide 59 text

@marcoGomier Publish the artifacts ./gradlew publish

Slide 60

Slide 60 text

@marcoGomier Publish the artifacts ./gradlew publish ./gradlew publishToMavenLocal

Slide 61

Slide 61 text

@marcoGomier How to publish: iOS

Slide 62

Slide 62 text

@marcoGomier 🥵

Slide 63

Slide 63 text

@marcoGomier XCFramework

Slide 64

Slide 64 text

@marcoGomier Build an XCFramework https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#build-xcframeworks import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework kotlin { ... val libName = "LibraryName" val xcf = XCFramework(libName) listOf( iosX64(), iosArm64(), iosSimulatorArm64() ).forEach { it.binaries.framework { baseName = libName xcf.add(this) isStatic = true } } }

Slide 65

Slide 65 text

@marcoGomier XCFramework: Gradle tasks assemble${libName}XCFramework assemble${libName}DebugXCFramework assemble${libName}ReleaseXCFramework

Slide 66

Slide 66 text

@marcoGomier Build an XCFramework

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

@marcoGomier Where to start

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

@marcoGomier 🧑🎨 You have a blank canvas

Slide 73

Slide 73 text

@marcoGomier Existing projects Common Kotlin Android App iOS App .aar XCFramework Android App Repository KMP Repository iOs App Repository

Slide 74

Slide 74 text

@marcoGomier 💡 Create a library!

Slide 75

Slide 75 text

@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?

Slide 76

Slide 76 text

@marcoGomier • DTOs • Common Models • Utility methods • Analytics • . . . Where to start?

Slide 77

Slide 77 text

@marcoGomier Start little

Slide 78

Slide 78 text

@marcoGomier Start little Go bigger then

Slide 79

Slide 79 text

@marcoGomier Start little then go bigger • Validate the process with “little” effort • Then you can go bigger and share more “features”

Slide 80

Slide 80 text

@marcoGomier Ecosystem

Slide 81

Slide 81 text

@marcoGomier https://android-developers.googleblog.com/2024/05/android-support-for-kotlin-multiplatform-to-share-business-logic-across-mobile-web-server-desktop.html?m=1

Slide 82

Slide 82 text

@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 ...

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

@marcoGomier https://www.jetbrains.com/ fl eet/

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

@marcoGomier Conclusions

Slide 91

Slide 91 text

@marcoGomier • Kotlin Multiplatform ! = Cross Platform • Stable since Kotlin 1.9.20 • It’s a joint and team approach Conclusions

Slide 92

Slide 92 text

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/luca992/multiplatform-swiftpackage • 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/

Slide 93

Slide 93 text

@marcoGomier Thank you! > Twitter: @marcoGomier > Github: prof18 > Website: marcogomiero.com > Mastodon: androiddev.social/@marcogom Marco Gomiero 👨💻 Senior Android Developer @ Airalo Google Developer Expert for Kotlin