Slide 1

Slide 1 text

@cafonsomota Kotlin through Android, iOS and Web A Multiplatform Triathlon

Slide 2

Slide 2 text

Android Dev Lead @WITSoftware Founder @GDGCoimbra and co-founder @Kotlin_Knights ✍ Author @rwenderlich and @opinioesonline Podcaster wannabe Loves travel, photography and running @cafonsomota

Slide 3

Slide 3 text

MATERIALS speakerdeck.com/cmota/a-multiplatform-triathlon github.com/cmota/kmp-a-multiplatform-triathlon cmota.github.io/kmp-codelabs @cafonsomota

Slide 4

Slide 4 text

@cafonsomota dev-reactions/commitstrip/unrealist-mobile-dev-dream

Slide 5

Slide 5 text

2008 2012 2018 2013 2011 2017 2006 2010 2009 2015 * Kotlin Multiplatform *

Slide 6

Slide 6 text

- Small learning curve for web developers - Smaller team - Typically half of the cost needed on native - Less time (working hours) to built (advantages) Cross-platform to test to fix issues (hopefully not framework specific )

Slide 7

Slide 7 text

- Chained to the framework implementation of UI - new updates from the OS will take time to adopt - Performance is not the same - Some native code might need to be written - OS/ device features are dependent on the fw support - Dart (flutter) is not widely used language (for now) - Committed to one framework/ language (disadvantages) Cross-platform

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

what about Kotlin Multiplatform?

Slide 10

Slide 10 text

shares application logic doesn’t share the application UI kotlin multiplatform

Slide 11

Slide 11 text

- Chained to the framework implementation of UI - new updates from the OS will take time to adopt - Performance is not the same - Some native code might need to be written - OS/ device features are dependent on the fw support - Dart (flutter) is not widely used language (for now) - Committed to one framework/ language (disadvantages) Cross-platform

Slide 12

Slide 12 text

- Chained to the framework implementation of UI - new updates from the OS will take time to adopt - Performance is not the same - Some native code might need to be written - OS/ device features are dependent on the fw support - Dart (flutter) is not widely used language (for now) - Committed to one framework/ language Cross-multiplatform UI is developed natively nope, UI is developed natively

Slide 13

Slide 13 text

- Chained to the framework implementation of UI - new updates from the OS will take time to adopt - Performance is not the same - Some native code might need to be written - OS/ device features are dependent on the fw support - Dart (flutter) is not widely used language (for now) - Committed to one framework/ language Cross-multiplatform nope, UI is developed natively UI is developed natively always arguable, but you’re leaving UI to native, so…

Slide 14

Slide 14 text

- Chained to the framework implementation of UI - new updates from the OS will take time to adopt - Performance is not the same - Some native code might need to be written - OS/ device features are dependent on the fw support - Dart (flutter) is not widely used language (for now) - Committed to one framework/ language Cross-multiplatform nope, UI is developed natively yes, the UI UI is developed natively always arguable, but you’re leaving UI to native, so…

Slide 15

Slide 15 text

- Chained to the framework implementation of UI - new updates from the OS will take time to adopt - Performance is not the same - Some native code might need to be written - OS/ device features are dependent on the fw support - Dart (flutter) is not widely used language (for now) - Committed to one framework/ language Cross-multiplatform nope, UI is developed natively yes, the UI UI is developed natively you have direct access to them; although if on shared module it might give you extra effort always arguable, but you’re leaving UI to native, so…

Slide 16

Slide 16 text

- Chained to the framework implementation of UI - new updates from the OS will take time to adopt - Performance is not the same - Some native code might need to be written - OS/ device features are dependent on the fw support - Dart (flutter) is not widely used language (for now) - Committed to one framework/ language Cross-multiplatform nope, UI is developed natively yes, the UI UI is developed natively kotlin, is one of the most trending languages nowadays, with strong community support always arguable, but you’re leaving UI to native, so… you have direct access to them; although if on shared module it might give you extra effort

Slide 17

Slide 17 text

(advantages) - Language features - Kotlin first! - Low risk - You decide what’s worth to share across platforms - Interoperability - Consistency across platforms - Strong investment from JetBrains and community support Kotlin Multiplatform server web native android desktop jvmMain jsMain androidMain iosMain macosX64Main linuxX64Main mingwX64Main

Slide 18

Slide 18 text

- It’s in alpha - Although we keep seeing new projects in production - Space was announced during Kotlin Conf 19 (web, desktop and mobile) (apps in production) Kotlin Multiplatform Space Adapted from: KotlinConf 2019: Opening Keynote by Andrey Breslav Full KMP Cash App Shares: business logic Yandex Maps Shares: business logic, wrappers for C++ libraries PlanGrid Planboard Workspace Shares: business logic, sync logic, mgmnt offline data Shares: business logic, networking, offline caching lyrs Shares: business logic

Slide 19

Slide 19 text

Kotlin Multiplatform

Slide 20

Slide 20 text

K otlin multiplatform using kotlin in projects that target more than one platform

Slide 21

Slide 21 text

network network parser parser model model presentation presentation model parser network view presentation view view presentation presentation web iOS android

Slide 22

Slide 22 text

business logic business logic business logic model parser network presentation model parser network presentation model parser network presentation view view view web iOS android

Slide 23

Slide 23 text

model parser network presentation common java/kotlin objective-c/ swift (kotlin) JS view view view web iOS android

Slide 24

Slide 24 text

dev-reactions/it-works.gif

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

but how can different platforms expect to communicate?

Slide 27

Slide 27 text

declared at common module expect declared at android module actual declared at iOS module declared at …

Slide 28

Slide 28 text

in which platform is my application running?

Slide 29

Slide 29 text

expect object Platform { val name: String } commonMain shared/src/commonMain/sample/Platform.kt

Slide 30

Slide 30 text

expect object Platform { val name: String } shared/src/commonMain/sample/Platform.kt commonMain - define actual on android - define actual on iOS - define actual on web targets: android, iOS and the web

Slide 31

Slide 31 text

import android.os.Build.MANUFACTURER actual object Platform { actual val name: String = $MANUFACTURER } shared/src/androidMain/sample/Platform.kt Platform-dependent code

Slide 32

Slide 32 text

import android.os.Build.MANUFACTURER actual object Platform { actual val name: String = $MANUFACTURER } shared/src/androidMain/sample/Platform.kt Platform-dependent code

Slide 33

Slide 33 text

import android.os.Build.MANUFACTURER actual object Platform { actual val name: String = $MANUFACTURER } shared/src/androidMain/sample/Platform.kt import platform.UIKit.UIDevice actual object Platform { actual val name: String = UIDevice.currentDevice().name } Platform-dependent code shared/src/iosMain/sample/Platform.kt

Slide 34

Slide 34 text

import android.os.Build.MANUFACTURER actual object Platform { actual val name: String = $MANUFACTURER } shared/src/androidMain/sample/Platform.kt import platform.UIKit.UIDevice actual object Platform { actual val name: String = UIDevice.currentDevice().name } Platform-dependent code import kotlinx.browser.window actual object Platform { actual val name: String = window.navigator.useragent } shared/src/iosMain/sample/Platform.kt shared/src/jsMain/sample/Platform.kt

Slide 35

Slide 35 text

Platform-dependent code Hello Google-Chrome Hello Pixel-XL Hello iOS-Simulator

Slide 36

Slide 36 text

androidApp/ iosApp/ shared/ web/

Slide 37

Slide 37 text

androidApp/ iosApp/ shared/ web/ main/ MainActivity.kt src/ java/ app.package.name/ activities/

Slide 38

Slide 38 text

src/ commonMain/ commonTest/ shared/ androidApp/ iosApp/ web/

Slide 39

Slide 39 text

src/ commonMain/ commonTest/ shared/ androidApp/ iosApp/ web/

Slide 40

Slide 40 text

androidMain androidTest src/ commonMain/ commonTest/ shared/ androidApp/ iosApp/ web/

Slide 41

Slide 41 text

src/ commonMain/ commonTest/ shared/ androidMain androidTest androidApp/ iosApp/ web/

Slide 42

Slide 42 text

src/ commonMain/ commonTest/ shared/ androidMain androidTest iosMain/ iosTest/ jsMain/ jsTest/

Slide 43

Slide 43 text

Let’s get everything together

Slide 44

Slide 44 text

build.gradle.kts (:shared) kotlin { sourceSets { val commonMain by getting { dependencies { implementation("io.ktor:ktor-client-core:1.4.0") implementation("io.ktor:ktor-client-json:1.4.0") implementation("io.ktor:ktor-client-serialization:1.4.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") implementation("com.squareup.sqldelight:runtime:1.4.3") implementation("com.russhwolf:multiplatform-settings:0.6.2") } } } }

Slide 45

Slide 45 text

build.gradle.kts (:shared) kotlin { android() sourceSets { val commonMain by getting { dependencies { implementation("io.ktor:ktor-client-core:1.4.0") implementation("io.ktor:ktor-client-json:1.4.0") implementation("io.ktor:ktor-client-serialization:1.4.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") implementation("com.squareup.sqldelight:runtime:1.4.3") implementation("com.russhwolf:multiplatform-settings:0.6.2") } val androidMain by getting { dependencies { implementation("androidx.preference:preference:1.1.1") implementation("io.ktor:ktor-client-android:1.4.0") implementation("io.ktor:ktor-client-okhttp:1.4.0") implementation("com.squareup.sqldelight:android-driver:1.4.3") } } }

Slide 46

Slide 46 text

build.gradle.kts (:shared) kotlin { android() val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false if (onPhone) { iosArm64("ios") } else { iosX64("ios") } cocoapods { summary = "Some description for a Kotlin/Native module" homepage = "Link to a Kotlin/Native module homepage" frameworkName = "shared" ios.deploymentTarget = "13.2" } sourceSets { val commonMain by getting { dependencies { implementation("io.ktor:ktor-client-core:1.4.0") implementation("io.ktor:ktor-client-json:1.4.0") implementation("io.ktor:ktor-client-serialization:1.4.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")

Slide 47

Slide 47 text

build.gradle.kts (:shared) kotlin { android() val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false if (onPhone) { iosArm64("ios") } else { iosX64("ios") } cocoapods { summary = "Some description for a Kotlin/Native module" homepage = "Link to a Kotlin/Native module homepage" frameworkName = "shared" ios.deploymentTarget = "13.2" } sourceSets { val commonMain by getting { dependencies { implementation("io.ktor:ktor-client-core:1.4.0") implementation("io.ktor:ktor-client-json:1.4.0") implementation("io.ktor:ktor-client-serialization:1.4.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") implementation("com.squareup.sqldelight:runtime:1.4.3") implementation("com.russhwolf:multiplatform-settings:0.6.2") } val androidMain by getting { dependencies { implementation("androidx.preference:preference:1.1.1") implementation("io.ktor:ktor-client-android:1.4.0") implementation("io.ktor:ktor-client-okhttp:1.4.0") implementation("com.squareup.sqldelight:android-driver:1.4.3") } } } val iosMain by getting { dependencies { implementation("io.ktor:ktor-client-ios:1.4.0") implementation("com.squareup.sqldelight:native-driver:1.4.3") } } } }

Slide 48

Slide 48 text

build.gradle.kts (:shared) kotlin { android() val onPhone = System.getenv("SDK_NAME")?.startsWith("iphoneos") ?: false if (onPhone) { iosArm64("ios") } else { iosX64("ios") } cocoapods { summary = "Some description for a Kotlin/Native module" homepage = "Link to a Kotlin/Native module homepage" frameworkName = "shared" ios.deploymentTarget = "13.2" } js { browser { } } sourceSets { val commonMain by getting { dependencies { implementation("io.ktor:ktor-client-core:1.4.0") implementation("io.ktor:ktor-client-json:1.4.0") implementation("io.ktor:ktor-client-serialization:1.4.1") implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") implementation("com.squareup.sqldelight:runtime:1.4.3") implementation("com.russhwolf:multiplatform-settings:0.6.2") } val androidMain by getting { dependencies { implementation("androidx.preference:preference:1.1.1") implementation("io.ktor:ktor-client-android:1.4.0") implementation("io.ktor:ktor-client-okhttp:1.4.0") implementation("com.squareup.sqldelight:android-driver:1.4.3") } } } val iosMain by getting { dependencies { implementation("io.ktor:ktor-client-ios:1.4.0") implementation("com.squareup.sqldelight:native-driver:1.4.3") } } val jsMain by getting { dependencies { implementation("io.ktor:ktor-client-js:1.4.0") } } } }

Slide 49

Slide 49 text

*.kt common expect JVM actual *.kt, *.java, *.jar Native actual *.kt, *C, Swift, Framework JS actual *.kt, *.js, NPM

Slide 50

Slide 50 text

dev-reactions/god-mode.gif

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

- User interface - RecyclerView and more (Android) - UITableViewController and more (iOS) - Depends on the framework used (web) - Network request - Parse response objects - Store on local database - Use system preferences Project structure

Slide 55

Slide 55 text

android iOS Room CoreData Retrofit Alamofire GSON/ Moshi JSONSerialization MVP, MVVM, MVI MVVM, ELM RxJava RxSwift Tests Tests Activity UIViewController RecyclerView UITableView web There are a lot of different frameworks here… - redux - Fetch - json-api-serializer …

Slide 56

Slide 56 text

SQLDelight ktor kotlinx.serialization MVP kotlinx.coroutines kotlin.test Activity UIViewController RecyclerView UITableView android iOS web

Slide 57

Slide 57 text

multiplatform ktor SQLDelight model kotlinx.serialization GetConferences SQLDriver dispatcher view presenter Settings presenter

Slide 58

Slide 58 text

multiplatform network database model parser domain platform specific view presenter presenter

Slide 59

Slide 59 text

Network class ConferencesAPI() { private val client = HttpClient() suspend fun fetchConfs() = client.get("$URL") src/commonMain/data/ConferencesAPI.kt ktor multiplatform network database android parser view platform specific presenter presenter domain model

Slide 60

Slide 60 text

src/commonMain/domain/model/Conference.kt model @Serializable data class Conference ( val name: String, val city: String, val country: String, val date: String, val logo: String, val website: String, val status: String) multiplatform network database android parser view platform specific presenter presenter domain model

Slide 61

Slide 61 text

domain class GetConferences(val api: ConferencesAPI) { suspend operator fun invoke( onSuccess: (List) "-> Unit, onFailure: (Exception) "-> Unit) { val result = api.fetchConfs() val confs = Json.decodeFromString>(result) coroutineScope { onSuccess(confs) } ""... } multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/domain/GetConferences.kt

Slide 62

Slide 62 text

domain class GetConferences(val api: ConferencesAPI) { suspend operator fun invoke( onSuccess: (List) "-> Unit, onFailure: (Exception) "-> Unit) { val result = api.fetchConfs() val confs = Json.decodeFromString>(result) coroutineScope { onSuccess(confs) } ""... } multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/domain/GetConferences.kt

Slide 63

Slide 63 text

domain class GetConferences(val api: ConferencesAPI) { suspend operator fun invoke( onSuccess: (List) "-> Unit, onFailure: (Exception) "-> Unit) { val result = api.fetchConfs() val confs = Json.decodeFromString>(result) coroutineScope { onSuccess(confs) } ""... } multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/domain/GetConferences.kt

Slide 64

Slide 64 text

domain class GetConferences(val api: ConferencesAPI) { suspend operator fun invoke( onSuccess: (List) "-> Unit, onFailure: (Exception) "-> Unit) { val result = api.fetchConfs() val confs = Json.decodeFromString>(result) coroutineScope { onSuccess(confs) } ""... } multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/domain/GetConferences.kt

Slide 65

Slide 65 text

src/commonMain/presentation/ConfsListPresenter.kt class ConfsListPresenter(val conferences: GetConferences) { lateinit var view: IConferenceData fun attachView(currView: IConferenceData) { view = currView fetchConfsList() } … presenter multiplatform network database android parser view platform specific presenter presenter domain model

Slide 66

Slide 66 text

src/commonMain/presentation/ConfsListPresenter.kt class ConfsListPresenter(val conferences: GetConferences) { lateinit var view: IConferenceData fun attachView(currView: IConferenceData) { view = currView fetchConfsList() } … presenter multiplatform network database android parser view platform specific presenter presenter domain model

Slide 67

Slide 67 text

src/commonMain/presentation/cb/IConferenceData.kt interface IConferenceData { fun onConferenceDataFetched(confs: List) fun onConferenceDataFailed(e: Exception) } presenter multiplatform network database android parser view platform specific presenter presenter domain model

Slide 68

Slide 68 text

src/commonMain/presentation/ConfsListPresenter.kt class ConfsListPresenter(val conferences: GetConferences) { lateinit var view: IConferenceData fun attachView(currView: IConferenceData) { view = currView fetchConfsList() } private fun fetchConfsList() { PresenterCoroutineScope(coroutineContext).launch { conferences( onSuccess = { view"?.onConferenceDataFetched(it) }, onFailure = { view"?.onConferenceDataFailed(it) }) presenter multiplatform network database android parser view platform specific presenter presenter domain model

Slide 69

Slide 69 text

object ServiceLocator { private val conferenceAPI by lazy { ConferencesAPI() } private val getConferences: GetConferences get() = GetConferences(conferenceAPI) val getConfsPresenter: ConfsListPresenter get() = ConfsListPresenter(getConferences) … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/ServiceLocator.kt

Slide 70

Slide 70 text

object ServiceLocator { private val conferenceAPI by lazy { ConferencesAPI() } private val getConferences: GetConferences get() = GetConferences(conferenceAPI) val getConfsPresenter: ConfsListPresenter get() = ConfsListPresenter(getConferences) … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/ServiceLocator.kt

Slide 71

Slide 71 text

src/commonAndroid/presenter/activities/MainActivity.kt android class MainActivity : AppCompatActivity(), IConferencesData { val presenter by lazy { ServiceLocator.getConfsPresenter } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) presenter.attachView(this) } override fun onConferenceDataFetched(list: List){ setup(list) … multiplatform network database android parser view platform specific presenter presenter domain model

Slide 72

Slide 72 text

src/commonAndroid/presenter/activities/MainActivity.kt android class MainActivity : AppCompatActivity(), IConferencesData { val presenter by lazy { ServiceLocator.getConfsPresenter } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) presenter.attachView(this) } override fun onConferenceDataFetched(list: List){ setup(list) … multiplatform network database android parser view platform specific presenter presenter domain model

Slide 73

Slide 73 text

@cafonsomota > Configure project :shared Kotlin Multiplatform Projects are an Alpha feature. cmota.github.io/kmp-codelabs

Slide 74

Slide 74 text

- Start small, don’t try to reach 100% of shared logic - perhaps by writing common tests to gain familiarity - one module at a time (network, database, image processing, etc.) - You can spend some time resolving compilation issues - specially if you try to target all platforms (how to start?) Kotlin Multiplatform - It’s in alpha - Lookout for plugins/ libraries updates

Slide 75

Slide 75 text

- evangelize the iOS team - select one module where something is more complex on iOS than on Android - KMP is not intended to share the UI - iOS developers can focus on Swift and frontend features - Cross platform solutions like Flutter or React Native they need to use Dart or JS - not possible to debug kotlin from Xcode - plugin for Xcode from TouchLab/ support mentioned on Kotlin Conf 19 (how to start?) Kotlin Multiplatform

Slide 76

Slide 76 text

- Strong community - A lot of people are using kotlin nowadays - Google and JetBrains are pushing Kotlin in - You can ask questions directly on https://kotlinlang.slack.com - 2x faster to develop your features business logic - 2x faster writing unit tests - One tech stack - Consistency across platforms (conclusions) Kotlin Multiplatform

Slide 77

Slide 77 text

- Ktor (networking) - Kotlinx.Coroutines - Kotlinx.Serialization - SqlDelight (database) - Multiplatform Settings - Stately (state management) (libraries) More information

Slide 78

Slide 78 text

- .../JetBrains/kotlinconf-app - .../touchlab/DroidconKotlin - …/touchlab/KaMPKit - .../wojtek-kalicinski/sudoku-android - .../joreilly/PeopleInSpace - .../jonnyzzz/kotlin-game-of-life - .../MarcinMoskala/WorkoutMPP (cool projects to follow at GitHub) More information

Slide 79

Slide 79 text

- Kotlin Slack - Kotlin by: https://jakewharton.com/presentations/ https://touchlab.co/kamp-kit-touchlab/ https://kmp.icerock.dev/ - Kotlin Conf 2019 videos https://www.youtube.com/watch?v=0xKTM0A8gdI - Kotlin 1.4 Online Event https://www.youtube.com/watch?v=PW-jkOLucjM https://www.youtube.com/watch?v=5QPPZV04-50 https://www.youtube.com/watch?v=KObxmllDzPY (useful links) More information

Slide 80

Slide 80 text

@cafonsomota cmota.github.io/kmp-codelabs/ cmota.github.io/kmp-codelabs

Slide 81

Slide 81 text

@cafonsomota speakerdeck.com/cmota/a-multiplatform-triathlon github.com/cmota/kmp-a-multiplatform-triathlon cmota.github.io/kmp-codelabs