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

Kotlin Multiplatform in Production

Kotlin Multiplatform in Production

Update on the state of the Kotlin Multiplatform universe, current libraries, and how to get started.

Kevin Galligan

November 19, 2018
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. Q3 Q2 Q4 Q1 Q2 2018 2019 0 .6 v0.7

    v0.8 v0.8.2 v0.9.3 IDE tooling! Coroutines?
  3. Q3 Q2 Q4 Q1 Q2 2018 2019 0 .6 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
  4. Q3 Q2 Q4 Q1 Q2 2018 2019 0 .6 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 Other samples/libraries Production deployments Compiler plugins! MT Coroutines!
  5. Q3 Q2 Q4 Q1 Q2 2018 2019 0 .6 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!
  6. expect val mainThread:Boolean actual val mainThread: Boolean get() = Looper.myLooper()

    === Looper.getMainLooper() actual val mainThread: Boolean get() = NSThread.isMainThread()
  7. expect val mainThread:Boolean actual val mainThread: Boolean get() = Looper.myLooper()

    === Looper.getMainLooper() actual val mainThread: Boolean get() = NSThread.isMainThread() actual val mainThread: Boolean = true
  8. expect fun currentTimeMillis():Long expect fun <B> backgroundTask(backJob:()-> B, mainJob:(B) ->

    Unit) expect fun backgroundTask(backJob:()->Unit) expect fun networkBackgroundTask(backJob:()->Unit) expect fun initContext():NativeOpenHelperFactory expect fun <T> goFreeze(a:T):T expect fun <T> T.freeze2(): T expect fun simpleGet(url:String):String expect fun logException(t:Throwable) expect fun settingsFactory(): Settings.Factory expect fun createUuid():String
  9. actual class Date(val date:java.util.Date) { actual fun toLongMillis(): Long =

    date.time } actual class DateFormatHelper actual constructor(format: String) { val dateFormatter = object : ThreadLocal<DateFormat>(){ override fun initialValue(): DateFormat = SimpleDateFormat(format) } actual fun toDate(s: String): Date = Date(dateFormatter.get()!!.parse(s)) actual fun format(d: Date): String = dateFormatter.get()!!.format(d.date) }
  10. fun initPlatformClient( staticFileLoader: (filePrefix: String, fileType: String) -> String?, analyticsCallback:

    (name: String, params: Map<String, Any>) -> Unit, clLogCallback: (s: String) -> Unit) {
  11. fun initPlatformClient( staticFileLoader: (filePrefix: String, fileType: String) -> String?, analyticsCallback:

    (name: String, params: Map<String, Any>) -> Unit, clLogCallback: (s: String) -> Unit) { AppContext.initPlatformClient ({filePrefix, fileType -> loadAsset("${filePrefix}.${fileType}")}, {name: String, params: Map<String, Any> -> val event = CustomEvent(name) //Loop Answers.getInstance().logCustom(event) }, { Log.w("MainApp", it) })
  12. let appContext = AppContext() appContext.doInitPlatformClient(staticFileLoader: loadAsset, analyticsCallback: analyticsCallback, clLogCallback: csLog)

    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 } }
  13. class TalkExamples{ var justCountingStuff:Int = 0 init { backgroundCall {

    //do something justCountingStuff++ }.freeze() } }
  14. class TalkExamples{ var justCountingStuff:Int = 0 init { backgroundCall {

    //do something justCountingStuff++ }.freeze() } }
  15. private val stateBox: AtomicReference<DetachedObjectGraph<Any>> = AtomicReference( DetachedObjectGraph(mode = TransferMode.SAFE, producer

    = { mutableListOf<E>() as Any }) ) private val lock = NSLock() internal fun withLockDetached(proc: (MutableList<E>) -> MutableList<E>) { lock.lock() try { stateBox.value = DetachedObjectGraph(mode = TransferMode.SAFE, producer = { val dataList = stateBox.value.attach() as MutableList<E> proc(dataList) as Any }) } finally { lock.unlock() } }
  16. val lambdas = AtomicReference<PlatformLambdas?>(null) fun initPlatformClient( staticFileLoader: (filePrefix: String, fileType:

    String) -> String?, analyticsCallback: (name: String, params: Map<String, Any>) -> Unit, clLogCallback: (s: String) -> Unit) { lambdas.value = PlatformLambdas( staticFileLoader, analyticsCallback, clLogCallback).freeze() }
  17. Multiplatform Definitions • freeze() method and frozen info • Atomics

    (Int, Long, Reference) • K/N state-related annotations (@ThreadLocal/ @SharedImmutable) • Shared Collections!
  18. targets { fromPreset(presets.jvm, 'jvm') // This preset is for iPhone

    emulator // Switch here to presets.iosArm64 to build library for iPhone device fromPreset(presets.iosX64, 'ios') { compilations.main.outputKinds('FRAMEWORK') } }
  19. targets { fromPreset(presets.jvm, 'jvm') // This preset is for iPhone

    emulator // Switch here to presets.iosArm64 to build library for iPhone device fromPreset(presets.macosX64, 'ios') /*{ compilations.main.outputKinds('FRAMEWORK') }*/ }
  20. Libraries Needed • File Access • Date (310 port) •

    Mocking (and other test support) • Reactive • UI State