Sharing is Caring- Getting Started with Kotlin Multiplatform

Sharing is Caring- Getting Started with Kotlin Multiplatform

*** Presented in KotlinConf 2019, Droidcon Singapore 2019, Droidcon Vietnam 2019 ***

As Android developers, often we work with a backend service. Often in our team, there is an iOS project or a Javascript project, with similar functionality, that is being developed by our teammate developers next door. How fun would it be to write Kotlin code once and share it with all platforms? It will save us so much time and effort in implementation and testing! Please meet Kotlin Multiplatform! It is experimental, and yet very powerful and awesome.

This session will focus on creating a full-stack Kotlin project that fits an Android app and a backend Kotlin component. You’ll learn how to share code between the components to build more efficient and robust applications. And your teammate next door will thank you as well 🙂

3142db3adb711e247e371153b5777e04?s=128

Britt Barak

July 07, 2019
Tweet

Transcript

  1. Copenhagen Denmark SHARING IS CARING INTRO TO KOTLIN MULTIPLATFORM BRITT

    BARAK NEXMO, VONAGE @brittBarak
  2. Copenhagen Denmark SHARING IS CARING INTRO TO KOTLIN MULTIPLATFORM BRITT

    BARAK NEXMO, VONAGE
  3. SHARING IS CARING

  4. SHARING IS CARING

  5. Drinks Gift Card Cake Balloons Emma`s GoodBye @brittBarak

  6. Drinks Gift Card Cake Balloons Emma`s GoodBye @brittBarak

  7. Drinks Emma`s GoodBye Balloons Britt: red are the best! Gift

    @brittBarak
  8. Drinks Emma`s GoodBye Balloons Jurgo: will go later Gift Britt:

    red are the best! @brittBarak
  9. Drinks Emma`s GoodBye Balloons Jurgo: good? Jurgo: will go later

    Britt: red are the best! @brittBarak
  10. Drinks Emma`s GoodBye Balloons On A Call : Jurgo &

    Britt Jurgo: good? Jurgo: will go later Britt: red are the best! @brittBarak
  11. Data Model Conversation

  12. Conversation member member member member member member member member member

    member Data Model
  13. Conversation member member member member member member member member member

    Data Model member
  14. Data Model Conversation Event Event Event Event Event Event Event

  15. Data Model Conversation Prosecco! Call Balloons Is it good? Drinks

    Will go tmrw Red is best! More prosecco!
  16. Emma`s GoodBye Conversation Drinks Drinks Balloons Balloons Red is best!

    Red is best! Will go tmrw Will go tmrw Call Call Prosecco! Prosecco! Is it good? Is it good? More prosecco! More prosecco!
  17. Conversation Drinks Balloons Red is best! Will go tmrw Call

    Prosecco! Is it good? More prosecco! Balloons Cake Balloons Emma`s GoodBye Red is best! Will go tmrw Call Prosecco! Is it good? More prosecco! Drinks
  18. Parse Sort Present OnNewEvent() @brittBarak

  19. Parse Sort Present @brittBarak

  20. Parse Sort Present @brittBarak

  21. Parse Sort Present Present Present @brittBarak

  22. Parse Sort Present Present Present Parse Sort Parse
 Sort @brittBarak

  23. Parse Sort Present Present Present @brittBarak

  24. @brittBarak Parse Sort Present Present Present Kotlin Multiplatform

  25. @brittBarak Parse Sort Present Present Present Kotlin Multiplatform Share only

    what you want, when you want.
  26. Parse Sort Present Present Present Kotlin Multiplatform @brittBarak

  27. Common Present Present Present Kotlin Multiplatform @brittBarak

  28. Common Target Target Target Kotlin Multiplatform @brittBarak

  29. @brittBarak

  30. @brittBarak JVM

  31. @brittBarak JVM *.class

  32. @brittBarak JVM *.class *.java MyClass.java

  33. @brittBarak JVM *.class *.java javac MyClass.java MyClass.class

  34. @brittBarak JVM *.class *.java *.kt javac

  35. @brittBarak JVM *.class *.java *.kt javac MyFile.kt

  36. @brittBarak JVM *.class *.java *.kt kotlinc javac MyFileKt.java MyFile.kt

  37. @brittBarak JVM *.class *.java *.kt kotlinc javac MyFileKt.java MyFile.kt InnerClass.java

  38. @brittBarak JVM *.class *.java *.kt kotlinc javac MyFileKt.java MyFile.kt InnerClass.java

  39. @brittBarak JVM *.class *.java *.kt kotlinc javac MyFile.kt InnerClass.class MyFileKt.class

  40. Common Target Target Target Kotlin Multiplatform @brittBarak

  41. Common kotlin/ JVM Target Target Kotlin Multiplatform @brittBarak

  42. Common kotlin/ JVM Target Target Kotlin Multiplatform Android Backend Desktop

    @brittBarak
  43. Common kotlin/ JVM kotlin/ JS Target Kotlin Multiplatform Web browser

    Node.JS Android Backend Desktop @brittBarak
  44. Common kotlin/ JVM kotlin/ JS kotlin/ Native Kotlin Multiplatform Web

    browser Node.JS Android Backend Desktop @brittBarak
  45. Common kotlin/ JVM kotlin/ JS kotlin/ Native Kotlin Multiplatform Android

    Backend Desktop Web browser Node.JS iOS Android native Desktop native Web assembly @brittBarak
  46. Get Started @brittBarak

  47. 1. Add plugin plugins { id("org.jetbrains.kotlin.multiplatform").version("1.3.61") } build.gradle @brittBarak

  48. 2. Set targets @brittBarak

  49. Supported platforms • jvm —> Kotlin/JVM • android —> Android

    applications and libraries • js —> Kotlin/JS • Kotlin/Native —> @brittBarak
  50. Supported platforms (Native) •androidNativeArm32 , androidNativeArm64 —> Android NDK •iosArm32,

    iosArm64, iosX64 —> iOS •linuxArm32Hfp, linuxMips32, linuxMipsel32, linuxX64 —> Linux •macosX64 —> MacOS •mingwX64 —> Windows •wasm32 —> WebAssembly @brittBarak
  51. 2. Set targets @brittBarak

  52. build.gradle plugins {…} kotlin { jvm() js("nodeJs") { … }

    } @brittBarak 2. Set targets
  53. build.gradle plugins {…} kotlin { jvm() js("nodeJs") { … }

    js(“browser") { … } } @brittBarak 2. Set targets
  54. For Android @brittBarak

  55. build.gradle kotlin { android() } @brittBarak

  56. plugins { id("org.jetbrains.kotlin.multiplatform").version("1.3.61") id(“com.android.application") // or (“com.android.library") } kotlin {

    android() } @brittBarak
  57. plugins { id("org.jetbrains.kotlin.multiplatform").version("1.3.61") id(“com.android.application") // or (“com.android.library”) } android {

    ... } kotlin { android() } @brittBarak
  58. 3. Source sets @brittBarak

  59. 3. Source sets sourceSets { common{} android{} jvm{} } @brittBarak

  60. 3. Source sets sourceSets { common{} android{} jvm{} } @brittBarak

  61. 3. Source sets sourceSets { commonMain{} commonTest{} androidMain{} androidTest{} }

    @brittBarak
  62. 3. Source sets sourceSets { commonMain { dependencies { implementation

    ‘org.jetbrains.kotlin:kotlin-stdlib-common’ //… } } commonTest{…} } @brittBarak
  63. sourceSets { commonMain {…} commonTest{…} androidMain { dependencies { implementation

    "androidx.lifecycle:lifecycle-extensions:2.0.0" implementation ‘com.nexmo.android:client-sdk:1.0.0' } } androidTest {..} 3. Source sets @brittBarak
  64. sourceSets { commonMain {…} commonTest{…} androidMain { dependencies { implementation

    'androidx.lifecycle:lifecycle-extensions:2.0.0' implementation ‘com.nexmo.android:client-sdk:1.0.0' } } androidTest {..} 3. Source sets @brittBarak
  65. Project Structure /src commonMain commonTest androidMain androidTest iosMain iosTest

  66. app—> common @brittBarak

  67. common: Parse Sort app: Present on New Event() @brittBarak

  68. app val eventListener: NexmoEventListener = object { override fun onCustomEvent(event:

    NexmoEvent) { parseTextEvent(event.id, event.text, this@ListActivity) } @brittBarak
  69. app val eventListener: NexmoEventListener = object { override fun onCustomEvent(event:

    NexmoEvent) { parseTextEvent(event.id, event.text, this@ListActivity) } @brittBarak
  70. app val eventListener: NexmoEventListener = object { override fun onCustomEvent(event:

    NexmoEvent) { processCustomEvent(event, this@ListActivity) }
  71. fun processCustomEvent(event, presenter) { } commonMain Balloon @brittBarak

  72. fun processCustomEvent(event, presenter) { val customEvent = Json.nonstrict. parse(MyCustomEvent.serializer(), event)

    } commonMain Drinks Balloon @brittBarak
  73. fun processCustomEvent(event, presenter) { val customEvent = … when (customEvent.type)

    { TYPE_ITEM_ADDED -> { presenter. handleItemAdded(customEvent) } } } commonMain Drinks Balloon @brittBarak
  74. fun processCustomEvent(event, presenter) { val customEvent = … when (customEvent.type)

    { TYPE_ITEM_ADDED -> { presenter. handleItemAdded(customEvent) } } } commonMain Drinks Balloon @brittBarak
  75. fun processCustomEvent(event, presenter) { val customEvent = … when (customEvent.type)

    { TYPE_ITEM_ADDED -> { presenter. handleItemAdded(customEvent) } } } commonMain Balloon @brittBarak Drinks
  76. fun processCustomEvent(event, presenter) { val customEvent = … when (customEvent.type)

    { TYPE_ITEM_ADDED -> {} TYPE_TEXT -> { } } } commonMain Drinks Balloon “Hi!” @brittBarak
  77. fun processCustomEvent(event, presenter) { val customEvent = … when (customEvent.type)

    { TYPE_ITEM_ADDED -> {} TYPE_TEXT -> { … presenter.handleNewMessage (customEvent, position) } } } commonMain Drinks Balloon @brittBarak “Hi!”
  78. interface EventsPresenter { fun handleItemAdded(event: MyCustomEvent) fun handleNewMessage(event: MyCustomEvent, position:

    Int) } • commonMain @brittBarak
  79. override fun handleNewMessage(event: MyCustomEvent, position: Int) { val itemUiModel =

    uiModel.items[position] itemUiModel?.events?.addItem(EventUiModel(event)) rvItems.adapter?.notifyItemChanged(position) } • app @brittBarak
  80. override fun handleNewMessage(event: MyCustomEvent, position: Int) { //… recyclerview.adapter.notifyItemChanged(position) }

    • app @brittBarak
  81. Skipping Presenter? @brittBarak

  82. common —> app @brittBarak

  83. expect / actual @brittBarak

  84. A word about architecture @brittBarak

  85. Data Domain Presentation

  86. Data Domain Presentation Platform Common expect/actual DataModel UseCase UiModel View

    Repository
  87. AddItem @brittBarak

  88. Android btnAdd.setOnClickListener { //… uiModel.conversationLiveData. value?.let { it -> AddItem().execute(

    tilItemName.editText?.text.toString()) } } @brittBarak
  89. Android btnAdd.setOnClickListener { //… uiModel.conversationLiveData. value?.let { it -> AddItem().execute(

    tilItemName.editText?.text.toString()) } } @brittBarak
  90. Android btnAdd.setOnClickListener { //… uiModel.conversationLiveData. value?.let { it -> AddItem().execute(itemName)

    } } @brittBarak
  91. Common/ AddItem.kt @brittBarak

  92. Common/ AddItem.kt fun execute(itemName: String) { val eventJson = createEventJson(itemName)

    sendCustomText(eventJson) } expect fun sendCustomText(eventJson: String) @brittBarak
  93. Common/ AddItem.kt fun execute(itemName: String) { val eventJson = createEventJson(itemName)

    sendCustomText(eventJson) } expect fun sendCustomText(eventJson: String) @brittBarak
  94. Common/ AddItem.kt fun execute(itemName: String) { val eventJson = createEventJson(itemName)

    sendCustomText(eventJson) } expect fun sendCustomText(eventJson: String) @brittBarak
  95. Common/ AddItem.kt fun execute(itemName: String) { val eventJson = createEventJson(itemName)

    sendCustomText(eventJson) } expect fun sendCustomText(eventJson: String) @brittBarak
  96. android/ AddItem.kt actual fun sendCustomText(eventJson: String) { conversation?.sendText(eventJson, listener) }

    @brittBarak
  97. android/ AddItem.kt actual fun sendCustomText(eventJson: String) { conversation?.sendText(eventJson, listener) }

    @brittBarak
  98. ios/ AddItem.kt actual fun sendCustomText(eventJson: String) { activeConversation? .sendText(eventJson, completion:

    … //… } } @brittBarak
  99. JVM/ AddItem.kt actual fun sendCustomText(eventJson: String) { coroutineScope.launch(ApplicationDispatcher) { try

    { nexmoApiClient.post<String> { url(pathSendEvent) body = TextContent(eventJson, ContentType.Application.Json) } // … } } @brittBarak
  100. JVM/ AddItem.kt actual fun sendCustomText(eventJson: String) { coroutineScope.launch(ApplicationDispatcher) { try

    { nexmoApiClient.post<String> { url(pathSendEvent) body = TextContent(eventJson, ContentType.Application.Json) } // … } } @brittBarak
  101. JVM/ AddItem.kt actual fun sendCustomText(eventJson: String) { coroutineScope.launch(ApplicationDispatcher) { try

    { nexmoApiClient.post<String> { url(pathSendEvent) body = TextContent(eventJson, ContentType.Application.Json) } // … } } @brittBarak
  102. expect / actual

  103. 1. Same location @brittBarak expect / actual Common/ AddItem.kt android/

    AddItem.kt ios/ AddItem.kt js/ AddItem.kt
  104. 1. Same location 2. Same signature @brittBarak expect / actual

  105. 1. Same location 2. Same signature @brittBarak expect / actual

    expect fun sendCustomText(eventJson: String) actual fun sendCustomText(eventJson: String)
  106. What can I use? @brittBarak

  107. Packages

  108. Packages

  109. Libraries •KotlinX (serialsation) •Ktor (networking) •Coroutines (async) •SQKDelight (persistency) •more…

    • https://github.com/AAkira/Kotlin-Multiplatform-Libraries
  110. KotlinConf-19 sessions Today • Kotlin Multiplatform In Action/ Alexandr Pogrebnyak

    - 14:00, Aud15 • Bridge The Physical World: Kotlin/Native On Raspberry Pi/ Qian Jin - 14:00, Aud12 • Your Multiplatform Kaptain Has Arrived/ Ahmed El-Helw - 15:00 , Aud12 • Shipping A Mobile Multiplatform Project On Ios & Android/ Ben Asher, Alec Strong - 16:15 Keynote • Kotlin Native Concurrency Explained/ Kevin Galligan- 17:15 , Aud12 Tomorrow: • I Walk The Line: What Parts Of An App Should Be In Kotlin Native…/Ellen Shapiro - 10:15, Aud 15 • The State Of Kotlin/Js / Sebastian Aigner - 13:00, Aud 15 • Going Native/ Ana Redmond - 14:00, Aud15 • Effective Kotlin-Swift Interoperability / John Rodriguez- 16:15 , Aud11 Hands On Labs - Preparing Your Kotlin Multiplatform Development Environment/ Big Nerd Ranch - 16:15
  111. Sum up •Share what you want, when you want. •Experimental

    •Prepare to explore •Popular (in a good way!) •Growing, evolving, improving
  112. Keep Sharing and caring

  113. Keep Sharing and caring Thank you! @BrittBarak Join the goodbye

    party:)
  114. #KotlinConf THANK YOU AND REMEMBER TO VOTE Britt Barak @brittBarak