Slide 1

Slide 1 text

Kotlin Multiplatform Intro Kevin Galligan

Slide 2

Slide 2 text

Touchlab

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

What is Kotlin Multiplatform?

Slide 5

Slide 5 text

kot·lin mul·ti·plat·form /ˌkätˈlin məltiˈplatfôrm,ˌkätˈlin məltīˈplatfôrm/ noun noun: kotlin multiplatform 1.optional, natively-integrated, open-source, code sharing platform, based on the popular, modern language kotlin. facilitates non-ui logic availability on many platforms.

Slide 6

Slide 6 text

kot·lin mul·ti·plat·form /ˌkätˈlin məltiˈplatfôrm,ˌkätˈlin məltīˈplatfôrm/ noun noun: kotlin multiplatform 1.optional, natively-integrated, open-source, code sharing platform, based on the popular, modern language kotlin. facilitates non-ui logic availability on many platforms. Oh, and JetBrains!

Slide 7

Slide 7 text

Optional Sharing Low risk. No Big Decisions.

Slide 8

Slide 8 text

Natively Integrated smooth interop

Slide 9

Slide 9 text

open source

Slide 10

Slide 10 text

Code Sharing not “cross platform”

Slide 11

Slide 11 text

popular

Slide 12

Slide 12 text

popular

Slide 13

Slide 13 text

popular

Slide 14

Slide 14 text

modern

Slide 15

Slide 15 text

Not UI well, not necessarily UI

Slide 16

Slide 16 text

-Kevin Galligan “Shared UI is a history of pain and failure. Shared logic is the history of computers.”

Slide 17

Slide 17 text

Many Platforms

Slide 18

Slide 18 text

Common JVM JS Native iOS Mac Linux Windows Android/NDK Wasm Others… Java-6 Java-8 Android Browser Node

Slide 19

Slide 19 text

IDE and Tooling JetBrains makes it

Slide 20

Slide 20 text

Why Kotlin? High Efficiency Low Risk Modern Language/Tools Highly Engaged Community

Slide 21

Slide 21 text

mobile is easy

Slide 22

Slide 22 text

mobile is easy

Slide 23

Slide 23 text

Technically, Though what is it?

Slide 24

Slide 24 text

Common JVM JS Native

Slide 25

Slide 25 text

Common JVM JS Native iOS Mac Linux Windows Android/NDK Wasm Others… Java-6 Java-8 Android Browser Node

Slide 26

Slide 26 text

Common JVM JS Native iOS Mac Linux Windows Android/NDK Wasm Others… Java-6 Java-8 Android Browser Node

Slide 27

Slide 27 text

Common JVM JS Native iOS Mac Linux Windows Android/NDK Wasm Others… Java-6 Java-8 Android Browser Node

Slide 28

Slide 28 text

Common JVM JS Native iOS Mac Linux Windows Android/NDK Wasm Others… Java-6 Java-8 Android Browser Node

Slide 29

Slide 29 text

Common JVM JS Native iOS Mac Linux Windows Android/NDK Wasm Others… Java-6 Java-8 Android Browser Node

Slide 30

Slide 30 text

Common JVM Native iOS Mac Linux Windows Android/NDK Wasm Others… Java-6 Java-8 Android

Slide 31

Slide 31 text

Common JVM Native

Slide 32

Slide 32 text

Common JVM Native

Slide 33

Slide 33 text

Common JVM Native iOS

Slide 34

Slide 34 text

Common JVM iOS

Slide 35

Slide 35 text

Common JVM iOS Framework

Slide 36

Slide 36 text

Common JVM iOS Framework Android

Slide 37

Slide 37 text

Common Android iOS Framework

Slide 38

Slide 38 text

Common Android iOS Framework Android Stuff iOS Stuff

Slide 39

Slide 39 text

Status

Slide 40

Slide 40 text

Q3 Q2 Q4 Q1 Q2 2018 2019 v0.7 v0.8 v0.8.2 v0.9.3 IDE tooling! Coroutines? K/N 1.0, Kotlin 1.3 Gradle 4.10+ Android Studio Compiler plugins! Other samples/libraries Production deployments Paid license/debugger Reactive Library Big Production Apps Webassembly stuff? MT Coroutines!

Slide 41

Slide 41 text

Q3 Q2 Q4 Q1 Q2 2019 2020 v1.3.30 v1.3.40 v1.3.50 v1.3.60? MT Coroutines? Many Threading Libraries Many Threading Blog Posts Increased Production Adoption Improved Tooling Stability Compiler Plugins? Date/Time?

Slide 42

Slide 42 text

Q3 Q2 Q4 Q1 Q2 2019 2020 v1.3.30 v1.3.40 v1.3.50 v1.3.60? MT Coroutines? Mainstream Date/Time?

Slide 43

Slide 43 text

Is This Ready? that’s more about your team

Slide 44

Slide 44 text

Multithreaded Coroutines need to change my slides

Slide 45

Slide 45 text

Sharing Code

Slide 46

Slide 46 text

Kotlin Embraces “Native” the interop story is really good

Slide 47

Slide 47 text

expect/actual

Slide 48

Slide 48 text

//In common code expect val isMainThread: Boolean

Slide 49

Slide 49 text

//In common code expect val isMainThread: Boolean //In Android/JVM actual val isMainThread: Boolean get() = Looper.getMainLooper() === Looper.myLooper()

Slide 50

Slide 50 text

//In common code expect val isMainThread: Boolean //In Android/JVM actual val isMainThread: Boolean get() = Looper.getMainLooper() === Looper.myLooper() //In iOS/native code actual val isMainThread: Boolean get() = NSThread.isMainThread()

Slide 51

Slide 51 text

//Value expect val isMainThread: Boolean

Slide 52

Slide 52 text

//Value expect val isMainThread: Boolean //Function expect fun myFun():String

Slide 53

Slide 53 text

//Value expect val isMainThread: Boolean //Function expect fun myFun():String //Class expect class MyClass { fun heyo(): String }

Slide 54

Slide 54 text

//Value expect val isMainThread: Boolean //Function expect fun myFun():String //Class expect class MyClass { fun heyo(): String } //Object expect object MyObject { fun heyo(): String }

Slide 55

Slide 55 text

//Value expect val isMainThread: Boolean //Function expect fun myFun():String //Class expect class MyClass { fun heyo(): String } //Object expect object MyObject { fun heyo(): String } //Annotation @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CONSTRUCTOR) @Retention(AnnotationRetention.SOURCE) expect annotation class Throws(vararg val exceptionClasses: KClass)

Slide 56

Slide 56 text

expect class Sample() { fun checkMe(): Int } expect object Platform { val name: String } fun hello(): String = "Hello from ${Platform.name}"

Slide 57

Slide 57 text

actual class Sample { actual fun checkMe() = 44 } actual object Platform { actual val name: String = "Android" }

Slide 58

Slide 58 text

actual class Sample { actual fun checkMe() = 7 } actual object Platform { actual val name: String = "iOS" }

Slide 59

Slide 59 text

actual typealias with great power…

Slide 60

Slide 60 text

/** * Multiplatform AtomicInt implementation */ expect class AtomicInt(initialValue: Int) { fun get(): Int fun set(newValue: Int) fun incrementAndGet(): Int fun decrementAndGet(): Int fun addAndGet(delta: Int): Int fun compareAndSet(expected: Int, new: Int): Boolean }

Slide 61

Slide 61 text

JVM Side?

Slide 62

Slide 62 text

import java.util.concurrent.atomic.AtomicInteger actual typealias AtomicInt = AtomicInteger

Slide 63

Slide 63 text

Actual needs to match because obviously it does

Slide 64

Slide 64 text

import kotlin.native.concurrent.AtomicInt actual class AtomicInt actual constructor(initialValue:Int){ private val atom = AtomicInt(initialValue) actual fun get(): Int = atom.value actual fun set(newValue: Int) { atom.value = newValue } actual fun incrementAndGet(): Int = atom.addAndGet(1) actual fun decrementAndGet(): Int = atom.addAndGet(-1) actual fun addAndGet(delta: Int): Int = atom.addAndGet(delta) actual fun compareAndSet(expected: Int, new: Int): Boolean = atom.compareAndSet(expected, new) }

Slide 65

Slide 65 text

import kotlin.native.concurrent.AtomicInt actual class AtomicInt actual constructor(initialValue:Int){ private val atom = AtomicInt(initialValue) actual fun get(): Int = atom.value actual fun set(newValue: Int) { atom.value = newValue } actual fun incrementAndGet(): Int = atom.addAndGet(1) actual fun decrementAndGet(): Int = atom.addAndGet(-1) actual fun addAndGet(delta: Int): Int = atom.addAndGet(delta) actual fun compareAndSet(expected: Int, new: Int): Boolean = atom.compareAndSet(expected, new) }

Slide 66

Slide 66 text

library talk

Slide 67

Slide 67 text

Prefer Interfaces for “service” objects

Slide 68

Slide 68 text

public interface Settings { public fun clear() public fun remove(key: String) public fun hasKey(key: String): Boolean public fun putInt(key: String, value: Int) public fun getInt(key: String, defaultValue: Int = 0): Int public fun getIntOrNull(key: String): Int? public fun putLong(key: String, value: Long) public fun getLong(key: String, defaultValue: Long = 0): Long public fun getLongOrNull(key: String): Long? //Etc... } from https://github.com/russhwolf/multiplatform-settings

Slide 69

Slide 69 text

expect fun platformSettings():Settings

Slide 70

Slide 70 text

object ServiceRegistry { var sessionizeApi:SessionizeApi by ThreadLocalDelegate() var analyticsApi: AnalyticsApi by FrozenDelegate() var notificationsApi:NotificationsApi by FrozenDelegate() var dbDriver: SqlDriver by FrozenDelegate() var cd: CoroutineDispatcher by FrozenDelegate() var appSettings: Settings by FrozenDelegate() var concurrent: Concurrent by FrozenDelegate() var timeZone: String by FrozenDelegate() //Etc… from https://github.com/touchlab/DroidconKotlin/

Slide 71

Slide 71 text

Just an Interface no expect/actual required

Slide 72

Slide 72 text

class TestSettings:Settings { private val map = frozenHashMap() override fun clear() { map.clear() } override fun getBoolean(key: String, defaultValue: Boolean): Bo return if(map.containsKey(key)){ map[key] as Boolean }else{ defaultValue } } //Etc… from https://github.com/touchlab/DroidconKotlin/

Slide 73

Slide 73 text

object ServiceRegistry { var sessionizeApi:SessionizeApi by ThreadLocalDelegate() var analyticsApi: AnalyticsApi by FrozenDelegate() var notificationsApi:NotificationsApi by FrozenDelegate() var dbDriver: SqlDriver by FrozenDelegate() var cd: CoroutineDispatcher by FrozenDelegate() var appSettings: Settings by FrozenDelegate() var concurrent: Concurrent by FrozenDelegate() var timeZone: String by FrozenDelegate() //Etc… from https://github.com/touchlab/DroidconKotlin/

Slide 74

Slide 74 text

koin multiplatform

Slide 75

Slide 75 text

interface AnalyticsApi { fun logEvent(name: String, params: Map) }

Slide 76

Slide 76 text

class AnalyticsApiImpl(val firebaseAnalytics: FirebaseAnalytics) : AnalyticsApi { override fun logEvent(name: String, params: Map) { val bundle = Bundle() params.keys.forEach { key -> when (val obj = params[key]) { is String -> bundle.putString(key, obj) is Int -> bundle.putInt(key, obj) else -> { throw IllegalArgumentException(“…”) } } } firebaseAnalytics.logEvent(name, bundle) } }

Slide 77

Slide 77 text

class AnalyticsApiMock : AnalyticsApi { var logCalled = false override fun logEvent(name: String, params: Map) { logCalled = true } }

Slide 78

Slide 78 text

class FirebaseAnalyticsApi: AnalyticsApi{ func logEvent(name: String, params: [String : Any]) { Analytics.logEvent(name, parameters: params) } }

Slide 79

Slide 79 text

serviceRegistry.doInitLambdas(staticFileLoader: loadAsset, clLogCallback: csLog, softExceptionCallback: softExceptionCallback) serviceRegistry.doInitServiceRegistry(sqlDriver: …, coroutineDispatcher: UI(), settings: FunctionsKt.defaultSettings(), concurrent: MainConcurrent(), sessionizeApi: SessionizeApiImpl(), analyticsApi: …, notificationsApi: NotificationsApiImpl(), timeZone: timeZone) AppContext().doInitAppContext(networkRepo: NetworkRepo(), fileRepo: FileRepo(), serviceRegistry: serviceRegistry, dbHelper: SessionizeDbHelper(), notificationsModel: NotificationsModel())

Slide 80

Slide 80 text

Function args! swift friendly

Slide 81

Slide 81 text

fun initLambdas( staticFileLoader: (filePrefix: String, fileType: String) -> String?, clLogCallback: (s: String) -> Unit, softExceptionCallback: (e:Throwable, message:String) - >Unit)

Slide 82

Slide 82 text

func loadAsset(filePrefix:String, fileType:String) -> String?{ do{ let bundleFile = Bundle.main.path(forResource: filePrefix, ofType: fileType) return try String(contentsOfFile: bundleFile!) } catch { return nil } }

Slide 83

Slide 83 text

Hard(er) to test tests are Kotlin exe

Slide 84

Slide 84 text

go.touchlab.co/dcktsrc

Slide 85

Slide 85 text

go.touchlab.co/dcktsrc

Slide 86

Slide 86 text

So no expect/actual? um…

Slide 87

Slide 87 text

Minimize expect/actual Droidcon App: 9 functions, 2 classes

Slide 88

Slide 88 text

Minimize expect/actual? Firestore SDK: 100+ ‘expect’ instances

Slide 89

Slide 89 text

Getting Started

Slide 90

Slide 90 text

Droidcon is OK there’s a lot going on, though

Slide 91

Slide 91 text

JetBrains Doc kotlinlang.org/docs/reference/building-mpp-with-gradle.html

Slide 92

Slide 92 text

“5 minutes” go.touchlab.co/kmp5

Slide 93

Slide 93 text

Kotlin Slack https://kotlinlang.org/community/

Slide 94

Slide 94 text

KMP evaluation kit

Slide 95

Slide 95 text

KaMP-Kit go.touchlab.co/KaMP-Kit

Slide 96

Slide 96 text

Concurrency it’s complicated (with native)

Slide 97

Slide 97 text

Two Rules mutable = 1 thread/immutable = many threads

Slide 98

Slide 98 text

Frozen new state of state

Slide 99

Slide 99 text

public fun T.freeze(): T {...}

Slide 100

Slide 100 text

public fun T.freeze(): T {...}

Slide 101

Slide 101 text

JetBrains Links • https://github.com/JetBrains/kotlin-native/blob/ master/IMMUTABILITY.md • https://github.com/JetBrains/kotlin-native/blob/ master/CONCURRENCY.md • https://www.youtube.com/watch?v=nw6YTfEyfO0

Slide 102

Slide 102 text

go.touchlab.co/knthreads

Slide 103

Slide 103 text

Kotlinconf

Slide 104

Slide 104 text

Thanks! @kpgalligan touchlab.co

Slide 105

Slide 105 text

Thanks! @kpgalligan touchlab.co Join the team !