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

Kotlin Multiplatform Intro

Kotlin Multiplatform Intro

Introduction to Kotlin Multiplatform, at Oredev.

Kevin Galligan

November 06, 2019
Tweet

More Decks by Kevin Galligan

Other Decks in Programming

Transcript

  1. 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.
  2. 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!
  3. -Kevin Galligan “Shared UI is a history of pain and

    failure. Shared logic is the history of computers.”
  4. Common JVM JS Native iOS Mac Linux Windows Android/NDK Wasm

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

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

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

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

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

    Others… Java-6 Java-8 Android Browser Node
  10. 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!
  11. 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?
  12. Q3 Q2 Q4 Q1 Q2 2019 2020 v1.3.30 v1.3.40 v1.3.50

    v1.3.60? MT Coroutines? Mainstream Date/Time?
  13. //In common code expect val isMainThread: Boolean //In Android/JVM actual

    val isMainThread: Boolean get() = Looper.getMainLooper() === Looper.myLooper()
  14. //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()
  15. //Value expect val isMainThread: Boolean //Function expect fun myFun():String //Class

    expect class MyClass { fun heyo(): String } //Object expect object MyObject { fun heyo(): String }
  16. //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<out Throwable>)
  17. expect class Sample() { fun checkMe(): Int } expect object

    Platform { val name: String } fun hello(): String = "Hello from ${Platform.name}"
  18. actual class Sample { actual fun checkMe() = 44 }

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

    actual object Platform { actual val name: String = "iOS" }
  20. /** * 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 }
  21. 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) }
  22. 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) }
  23. 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
  24. 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/
  25. class TestSettings:Settings { private val map = frozenHashMap<String, Any?>() 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/
  26. 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/
  27. class AnalyticsApiImpl(val firebaseAnalytics: FirebaseAnalytics) : AnalyticsApi { override fun logEvent(name:

    String, params: Map<String, Any>) { 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) } }
  28. class AnalyticsApiMock : AnalyticsApi { var logCalled = false override

    fun logEvent(name: String, params: Map<String, Any>) { logCalled = true } }
  29. 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())
  30. fun initLambdas( staticFileLoader: (filePrefix: String, fileType: String) -> String?, clLogCallback:

    (s: String) -> Unit, softExceptionCallback: (e:Throwable, message:String) - >Unit)
  31. 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 } }