The Hitchhikers Guide Through Kotlin Multiplatform

D8a3623b157508fecdae1f8e756f362f?s=47 cmota
June 21, 2019

The Hitchhikers Guide Through Kotlin Multiplatform

Since the early days of mobile that we keep seeing new frameworks being designed to overcome one of the biggest challenges:
- How can I develop for both Android and iOS?

Although it’s initial promises, when we talk about performance, maintainability or even customization we keep discarding these solutions and we always choose native.

Fast forward to the present, and now we have two new languages: Android is Kotlin first and iOS, Swift. And if you put them side by side you can see a lot of similarities between both what will ease switching between one to the other if you have to develop for both platforms.

But what I told that you could just develop in Kotlin and run it seamlessly on all devices? Here comes Kotlin Multiplatform!

D8a3623b157508fecdae1f8e756f362f?s=128

cmota

June 21, 2019
Tweet

Transcript

  1. 4.
  2. 5.

    a brief history of time “In the beginning the Universe

    was created. This had made many people angry and has been widely regarded as a bad move.”
  3. 10.

    Photo by Fabian Grohs on Unsplash a wild application idea

    appears! develop it for Android and iOS.
  4. 11.

    -hire a team of specialised developers -define requirements -plan features

    development -start development/ write unit tests -write t-specs for SQA validation -cross-checking validation between both platforms (or jacks) Steps
  5. 12.

    ~2xthe team the cost the status meetings required the time

    spent on development the time spent on testing native the time spent on bug fixing
  6. 13.

    Photo by Fabian Grohs on Unsplash a wild application idea

    appears! develop it for Android and iOS and web.
  7. 14.

    ~3xthe team the cost the status meetings required the time

    spent on development the time spent on testing the time spent on bug fixing
  8. 15.
  9. 18.

    - smaller team - typically half of the cost needed

    on native - small learning curve for web developers (advantages) Cross-platform
  10. 19.

    - 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 is not widely used language (for now) (disadvantages) Cross-platform
  11. 23.
  12. 24.

    - developed by JetBrains - open source - concise, safe,

    interoperable, tool-friendly - supported/ used by Google for Android Development - it’s more than “just for Android” Kotlin Server-side Android Kotlin JS Native
  13. 27.

    public class Conference { private String mName; private String mLocal;

    private List<String> mTechs; public Conference(String name, String local, List<String> techs) { mName = name; mLocal = local; mTechs = techs; } public String getName() { return mName; } public void setName(String name) { Conference.java (less code) Concise
  14. 28.

    data class Conference (val name: String, val location: String, val

    techs: List<String>) (less code) Concise Conference.kt
  15. 30.

    -getOrElse() -find() -filter() -filterNot() -filterNotNull() -flatMap() -mapNotNull() -all() -any() -sumBy()

    -zip() -… -take() -takeLast() -sortBy() -sortByDescending() -groupBy() -map() (collections) Language features
  16. 31.

    mutableListOf(1, null, 2, null, 3, 4, 5, 6, 7, 8,

    9)
 
 
 > [1, null, 2, null, 3, 4, 5, 6, 7, 8, 9] (collections) Language features output
  17. 32.

    > [1, 2, 3, 4, 5, 6, 7, 8, 9]

    (collections) Language features output mutableListOf(1, null, 2, null, 3, 4, 5, 6, 7, 8, 9)
 .filterNotNull()
 

  18. 33.

    > [2, 4, 6, 8] mutableListOf(1, null, 2, null, 3,

    4, 5, 6, 7, 8, 9)
 .filterNotNull()
 .filter { it % 2 == 0 }
 (collections) Language features output
  19. 34.

    > [8, 6, 4, 2] mutableListOf(1, null, 2, null, 3,

    4, 5, 6, 7, 8, 9)
 .filterNotNull()
 .filter { it % 2 == 0 }
 .sortedDescending() (collections) Language features output
  20. 35.

    - small learning curve involved - easily to go from

    JavaScript/ Swift into Kotlin and back (from a developers’ point of view) Easy to learn
  21. 36.

    var variable = 42 variable = 1 let value =

    42 var variable = 42 variable = 1 val value = 42 *adapted from http://nilhcem.com/swift-is-like-kotlin/ (variables and constants) Kotlin vs Swift Kotlin Swift
  22. 37.

    fun greet(name: String, day: String): String { return "Hello $name,

    today is $day." } greet(“Porto", “Saturday") *updated from http://nilhcem.com/swift-is-like-kotlin/ func greet(_ name: String,_ day: String) -> String { return "Hello \(name), today is \(day)." } greet(“Porto", “Saturday") Kotlin Swift (functions) Kotlin vs Swift
  23. 38.

    kotlin multiplatform “‘Resistance is useless!’ How can anyone maintain a

    positive mental attitude if you’re saying things like that?”
  24. 40.

    (advantages) - language features - kotlin first! - low risk

    - you decide what’s worth to share across projects - interoperability - consistency across platforms - strong community support Kotlin Multiplatform
  25. 42.

    view view view view model parser network presentation presentation presentation

    presentation network network network parser parser parser model model model presentation presentation presentation desktop web iOS android
  26. 43.

    business logic business logic business logic model parser network presentation

    model parser network presentation model parser network presentation model parser network presentation business logic view view view view desktop web iOS android
  27. 44.

    android iOS web desktop model parser network presentation common view

    view view view java/kotlin objective-c/ swift (kotlin) JS supported in jvm
  28. 46.
  29. 49.

    commonMain src/commonMain/sample/Platform.kt - define actual on android - define actual

    on iOS targets: android and iOS expect object Platform { val name: String }
  30. 50.

    src/iOSMain/sample/Platform.kt actual object Platform { actual val name: String =

    "Android" } actual object Platform { actual val name: String = "iOS" } src/androidMain/sample/Platform.kt platform-dependent code
  31. 52.

    *.kt common expect JVM actual *.kt, *.java, *.jar Native actual

    *.kt, *C, Swift, Framework JS actual *.kt, *.js, NPM
  32. 54.

    demo "For a moment, nothing happened. Then, after a second

    or so, nothing continued to happen.”
  33. 56.

    - implement UI - RecyclerView (Android)/ UITableViewController (iOS) - make

    network request - parse response object - notify the UI - that there’s new content available to be draw - reload the list project structure
  34. 57.

    android iOS Retrofit Alamofire GSON/ Moshi JSONSerialization MVP, MVVM, MVI

    MVVM, ELM RxJava RxSwift Tests Tests Activity UIViewController RecyclerView UITableView
  35. 58.

    Retrofit Alamofire GSON/ Moshi JSONSerialization MVP, MVVM, MVI MVVM, ELM

    RxJava RxSwift Tests Tests Activity UIViewController RecyclerView UITableView android iOS
  36. 59.

    Retrofit Alamofire GSON/ Moshi JSONSerialization MVP, MVVM, MVI MVVM, ELM

    RxJava RxSwift Tests Tests android iOS Activity UIViewController RecyclerView UITableView unnecessary duplication
  37. 60.

    class CommitAPI(engine: HttpClientEngine) { private val client = HttpClient(engine) {

    install(JsonFeature) { serializer = KotlinxSerializer() } } suspend fun fetchSpeakers(): AllSpeakersEntity { val response = client.get<HttpResponse>{ url ("$URL")} val json = response.readText() return Json.parse(SpeakersEntity.serializer(), json) } } network src/commonMain/data/CommitAPI.kt android business logic model parser network presentation view
  38. 61.

    class CommitAPI(engine: HttpClientEngine) { private val client = HttpClient(engine) {

    install(JsonFeature) { serializer = KotlinxSerializer() } } suspend fun fetchSpeakers(): AllSpeakersEntity { val response = client.get<HttpResponse>{ url ("$URL")} val json = response.readText() return Json.parse(SpeakersEntity.serializer(), json) } } parser src/commonMain/data/CommitAPI.kt android business logic model parser network presentation view
  39. 62.

    { “speaker": "Carlos Mota", "company": "WIT SOFTWARE", "title": "Lead Software

    Engineer", "bio": “... GDG Coimbra organizer and Kotlin evangelist, he also has a huge passion for travel, photography, space and the occasional run.", "talk_title": "The Hitchhikers Guide Through Kotlin Multiplatform", “talk_description”:”... But what I told that you could just develop in Kotlin and run it seamlessly on all devices? Here comes Kotlin Multiplatform!", "talk_time":"16:00", "img": “https://commitporto.com/static/speakers/carlos.jpg" ... } json response http://gist.github.com/cmota/…commit-speakers.json
  40. 63.

    { "speaker": "Carlos Mota", "company": "WIT SOFTWARE", "title": "Lead Software

    Engineer", "bio": "... GDG Coimbra organizer and Kotlin evangelist, he also has a huge passion for travel, photography, space and the occasional run.", "talk_title": "The Hitchhikers Guide Through Kotlin Multiplatform", "talk_description”:"... But what I told that you could just develop in Kotlin and run it seamlessly on all devices? Here comes Kotlin Multiplatform!", "talk_time":"16:00", "img": "https://commitporto.com/static/speakers/carlos.jpg" ... } json response http://gist.github.com/cmota/…commit-speakers.json
  41. 64.

    @Serializable data class Speaker(val speaker: String, val company: String, val

    title: String, val bio: String, @SerialName("talk_title") val title: String, @SerialName("talk_description") val description: String, @SerialName("talk_time") val schedule: String, val img: String) model src/commonMain/data/Speaker.kt ktor-client-serialization android business logic model parser network presentation view
  42. 65.

    class SpeakersListPresenter(val speakers: GetSpeakers) { lateinit var view: ISpeakersListView fun

    attachView(appView: ISpeakersListView) { view = appView fetchSpeakersList() } … presenter src/commonMain/presentation/SpeakersListPresenter.kt android business logic model parser network presentation view
  43. 66.

    interface ISpeakerEvent { fun onNewList(speakers: List<Speaker>) fun onNewListFailed(e: Exception) }

    presenter src/commonMain/presentation/ISpeakerEvent.kt android business logic model parser network presentation view
  44. 67.

    class SpeakersListPresenter(val speakers: GetSpeakers) { lateinit var view: ISpeakersListView fun

    attachView(appView: ISpeakersListView) { view = appView fetchSpeakersList() } … presenter src/commonMain/presentation/SpeakersListPresenter.kt android business logic model parser network presentation view onCreate()/ viewDidLoad()
  45. 68.

    fun fetchSpeakersList() { PresenterCoroutineScope(defaultDispatcher).launch { speakers( onSuccess = { view.onSpeakersListFetched(it)

    }, onFailure = { view.onSpeakersListFailed(it) }) } } presenter src/commonMain/presentation/SpeakersListPresenter.kt android business logic model parser network presentation view
  46. 69.

    class GetSpeakers(val commitAPI: CommitAPI) { suspend operator fun invoke( onSuccess:

    (List<Speaker>) -> Unit, onFailure: (Exception) -> Unit) { val result = commitAPI.fetchSpeakers() coroutineScope { launch(uiDispatcher) { try { onSuccess(result.speakers) } catch (e: Exception) { onFailure(e) } … } domain src/commonMain/domain/GetSpeakers.kt android business logic model parser network presentation view
  47. 70.
  48. 71.

    class MainActivity : AppCompatActivity(), ISpeakersEvent { val presenter by lazy

    { ServiceLocator.getPresenter } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) presenter.attachView(this) } override fun onNewList(speakers: List<Speaker>) { setUiComponents(speakers) } ... android src/commonAndroid/presentation/MainActivity.kt android business logic model parser network presentation view
  49. 72.

    class MainActivity : AppCompatActivity(), ISpeakersEvent { val presenter by lazy

    { ServiceLocator.getPresenter } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) presenter.attachView(this) } override fun onNewList(speakers: List<Speaker>) { setUiComponents(speakers) } ... android src/commonAndroid/presentation/MainActivity.kt android business logic model parser network presentation view
  50. 73.

    object ServiceLocator { val commitApi = CommitAPI(PlatformServiceLocator.httpClientEngine) val getPresenter: SpeakersListPresenter

    get() = SpeakersListPresenter(GetSpeakers(commitApi)) } expect object PlatformServiceLocator { val httpClientEngine: HttpClientEngine } init src/commonMain/ServiceLocator.kt android business logic model parser network presentation view
  51. 74.

    actual object PlatformServiceLocator { actual val httpClientEngine: HttpClientEngine by lazy

    { OkHttp.create () } } init src/mainAndroid/PlatformServiceLocator.kt android business logic model parser network presentation view
  52. 75.

    actual object PlatformServiceLocator { actual val httpClientEngine: HttpClientEngine by lazy

    { Ios.create() } } init src/iosMain/PlatformServiceLocator.kt iOS business logic model parser network presentation view
  53. 76.

    override fun onBindViewHolder(viewHolder: SpeakerViewHolder, position: Int) { val speaker =

    speakers[position] Glide.with(viewHolder.speakerPhoto) .load(speaker.img) .apply(RequestOptions.circleCropTransform()) .into(viewHolder.speakerPhoto) viewHolder.speakerName.text = speaker.speaker viewHolder.talkTitle.text = speaker.talkTitle viewHolder.container.setOnClickListener { action.onUserClickAction(speaker, it) } } android src/mainAndroid/presentation/SpeakersListAdapter.kt android business logic model parser network presentation view
  54. 77.

    override fun onBindViewHolder(viewHolder: SpeakerViewHolder, position: Int) { val speaker =

    speakers[position] Glide.with(viewHolder.speakerPhoto) .load(speaker.img) .apply(RequestOptions.circleCropTransform()) .into(viewHolder.speakerPhoto) viewHolder.speakerName.text = speaker.speaker viewHolder.talkTitle.text = speaker.talkTitle viewHolder.container.setOnClickListener { action.onUserClickAction(speaker, it) } } android src/mainAndroid/presentation/SpeakersListAdapter.kt android business logic model parser network presentation view you can keep using your android libraries
  55. 78.

    class SpeakerTableViewController: UITableViewController, ISpeakersListView { lazy var presenter = ServiceLocator.init().getPresenter

    var speakers = [Speaker]() override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) presenter.attachView(view: self) } … iOS iosApp/iosApp/SpeakerTableViewController.swift iOS business logic model parser network presentation view
  56. 79.

    class SpeakerTableViewController: UITableViewController, ISpeakersListView { lazy var presenter = ServiceLocator.init().getPresenter

    var speakers = [Speaker]() override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) presenter.attachView(view: self) } … iOS iosApp/iosApp/SpeakerTableViewController.swift iOS business logic model parser network presentation view
  57. 80.

    func onSpeakersListFetched(speakers: [Speaker]) { self.speakers = speakers self.tableView.reloadData() } iOS

    iosApp/iosApp/SpeakerTableViewController.swift iOS business logic model parser network presentation view
  58. 81.

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

    { let cellIdentifier = "SpeakerTableViewCell" let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as SpeakerTableViewCell let speaker = speakers[indexPath.row] cell.talkName.text = speaker.talkTitle cell.speakerName.text = speaker.speaker cell.speakerImage.image = speaker.img return cell } iOS iosApp/iosApp/SpeakerTableViewController.swift iOS business logic model parser network presentation view
  59. 83.

    - it’s in experimental state - although there are projects

    in production - you can spend some time resolving compilation issues - specially if you try to target all platforms - not possible to debug kotlin from Xcode - lookout for plugins/ libraries updates (in progress) Kotlin Multiplatform
  60. 84.

    - 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
  61. 85.

    - be extremely careful with all updates - IDEA, jdk,

    kotlin, libraries - everything. - start small, don’t try to reach 100% of shared logic - keep versioning in mind - remember that’s still on experimental state (suggestions) Kotlin Multiplatform
  62. 86.

    - Kotlin Slack - Android @ Portugal Slack - Kotlin

    Official - Kotlin Training - Kotlin by: https://jakewharton.com/presentations/ http://antonioleiva.com/kotlin (useful links) more information