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

The Hitchhikers Guide Through Kotlin Multiplatform

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!

cmota

June 21, 2019
Tweet

More Decks by cmota

Other Decks in Technology

Transcript

  1. Kotlin Multiplatform

    View Slide

  2. The Hitchhikers Guide Through
    Kotlin Multiplatform
    The Hitchhikers Guide Through
    Kotlin Multiplatform
    @cafonsomota

    View Slide

  3. speakerdeck.com/cmota/
    the-hitchhikers-guide-through-kotlin-multiplatform
    @cafonsomota

    View Slide

  4. View Slide

  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.”

    View Slide

  6. android iOS

    View Slide

  7. Dark Theme

    View Slide

  8. android iOS

    View Slide

  9. Photo by Fabian Grohs on Unsplash

    View Slide

  10. Photo by Fabian Grohs on Unsplash
    a wild application idea
    appears!
    develop it for Android and iOS.

    View Slide

  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

    View Slide

  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

    View Slide

  13. Photo by Fabian Grohs on Unsplash
    a wild application idea
    appears!
    develop it for Android and iOS and web.

    View Slide

  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

    View Slide

  15. View Slide

  16. let’s find a solution
    that works on all
    platforms.

    View Slide

  17. 2008 2012 2018
    2013
    2011 2017
    2006 2010
    2009 2015
    * Kotlin Multiplatform
    *

    View Slide

  18. - smaller team
    - typically half of the cost needed on native
    - small learning curve for web developers
    (advantages)
    Cross-platform

    View Slide

  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

    View Slide

  20. how can we have
    the best of both
    worlds?

    View Slide

  21. how can we have
    the best of both
    worlds?

    View Slide

  22. kotlin
    “42.”

    View Slide

  23. View Slide

  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

    View Slide

  25. Mutability
    etc.
    Null safety
    Default
    values
    Type
    inference
    String
    interpolation
    when
    Collections

    View Slide

  26. NullPointerException
    NullPointerException
    NullPointerException
    NullPointerException
    NullPointerException
    NullPointerException
    NullPointerException
    NullPointerException
    TransactionTooLargeException

    View Slide

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

    View Slide

  28. data class Conference (val name: String,
    val location: String,
    val techs: List)
    (less code)
    Concise
    Conference.kt

    View Slide

  29. Less bugs!
    more free time
    (and happier team… and management)
    (less code)
    Concise

    View Slide

  30. -getOrElse()
    -find()
    -filter()
    -filterNot()
    -filterNotNull()
    -flatMap()
    -mapNotNull()
    -all()
    -any()
    -sumBy()
    -zip()
    -…
    -take()
    -takeLast()
    -sortBy()
    -sortByDescending()
    -groupBy()
    -map()
    (collections)
    Language features

    View Slide

  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

    View Slide

  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()


    View Slide

  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

    View Slide

  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

    View Slide

  35. - small learning curve involved
    - easily to go from JavaScript/ Swift into Kotlin and back
    (from a developers’ point of view)
    Easy to learn

    View Slide

  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

    View Slide

  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

    View Slide

  38. kotlin multiplatform
    “‘Resistance is useless!’ How can anyone maintain a positive mental
    attitude if you’re saying things like that?”

    View Slide

  39. K
    otlin multiplatform
    using kotlin in projects
    that target more than
    one platform

    View Slide

  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

    View Slide

  41. first reaction

    View Slide

  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

    View Slide

  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

    View Slide

  44. android iOS web desktop
    model
    parser
    network
    presentation
    common
    view view view view
    java/kotlin objective-c/ swift (kotlin) JS supported in jvm

    View Slide

  45. but how can they expect to communicate?

    View Slide

  46. declared at common module
    expect
    declared at android module
    actual
    declared at iOS module
    declared at …

    View Slide

  47. we want a platform-specific value for name

    View Slide

  48. expect object Platform {
    val name: String
    }
    commonMain
    src/commonMain/sample/Platform.kt

    View Slide

  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
    }

    View Slide

  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

    View Slide

  51. IntelliJ will ask you declare the implementations for actual

    View Slide

  52. *.kt
    common
    expect
    JVM
    actual
    *.kt, *.java, *.jar
    Native
    actual
    *.kt, *C, Swift, Framework
    JS
    actual
    *.kt, *.js, NPM

    View Slide

  53. medium.com/@cafonsomota

    View Slide

  54. demo
    "For a moment, nothing happened. Then, after a second or so, nothing
    continued to happen.”

    View Slide

  55. github.com/cmota/commit19

    View Slide

  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

    View Slide

  57. android iOS
    Retrofit Alamofire
    GSON/ Moshi JSONSerialization
    MVP, MVVM, MVI MVVM, ELM
    RxJava RxSwift
    Tests Tests
    Activity UIViewController
    RecyclerView UITableView

    View Slide

  58. Retrofit Alamofire
    GSON/ Moshi JSONSerialization
    MVP, MVVM, MVI MVVM, ELM
    RxJava RxSwift
    Tests Tests
    Activity UIViewController
    RecyclerView UITableView
    android iOS

    View Slide

  59. Retrofit Alamofire
    GSON/ Moshi JSONSerialization
    MVP, MVVM, MVI MVVM, ELM
    RxJava RxSwift
    Tests Tests
    android iOS
    Activity UIViewController
    RecyclerView UITableView
    unnecessary duplication

    View Slide

  60. class CommitAPI(engine: HttpClientEngine) {
    private val client = HttpClient(engine) {
    install(JsonFeature) {
    serializer = KotlinxSerializer()
    }
    }
    suspend fun fetchSpeakers(): AllSpeakersEntity {
    val response = client.get{ 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

    View Slide

  61. class CommitAPI(engine: HttpClientEngine) {
    private val client = HttpClient(engine) {
    install(JsonFeature) {
    serializer = KotlinxSerializer()
    }
    }
    suspend fun fetchSpeakers(): AllSpeakersEntity {
    val response = client.get{ 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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  66. interface ISpeakerEvent {
    fun onNewList(speakers: List)
    fun onNewListFailed(e: Exception)
    }
    presenter
    src/commonMain/presentation/ISpeakerEvent.kt
    android
    business logic
    model
    parser
    network
    presentation
    view

    View Slide

  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()

    View Slide

  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

    View Slide

  69. class GetSpeakers(val commitAPI: CommitAPI) {
    suspend operator fun invoke(
    onSuccess: (List) -> 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

    View Slide

  70. View Slide

  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) {
    setUiComponents(speakers)
    }
    ...
    android
    src/commonAndroid/presentation/MainActivity.kt
    android
    business logic
    model
    parser
    network
    presentation
    view

    View Slide

  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) {
    setUiComponents(speakers)
    }
    ...
    android
    src/commonAndroid/presentation/MainActivity.kt
    android
    business logic
    model
    parser
    network
    presentation
    view

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  80. func onSpeakersListFetched(speakers: [Speaker]) {
    self.speakers = speakers
    self.tableView.reloadData()
    }
    iOS
    iosApp/iosApp/SpeakerTableViewController.swift
    iOS
    business logic
    model
    parser
    network
    presentation
    view

    View Slide

  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

    View Slide

  82. conclusions
    don’t forget to bring your own towel.

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  87. @cafonsomota
    so long and thanks for all the fish

    View Slide