The Hitchhikers Guide Through Kotlin Multiplatform Android Makers

D8a3623b157508fecdae1f8e756f362f?s=47 cmota
April 20, 2020

The Hitchhikers Guide Through Kotlin Multiplatform Android Makers

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

April 20, 2020
Tweet

Transcript

  1. Android Makers: Virtual Edition The Hitchhikers Guide Through Kotlin Multiplatform

    @cafonsomota
  2. hello . @cafonsomota

  3. Questions? bit.ly/kmp-androidmakers @cafonsomota

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

  5. None
  6. 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.”
  7. android iOS

  8. Photo by Fabian Grohs on Unsplash

  9. Photo by Fabian Grohs on Unsplash a wild application idea

    appears! develop it for Android and iOS.
  10. -hire a team of specialised developers -define requirements -design mockups

    for both platforms -plan features development -start development/ write unit tests -write t-specs for SQA validation -cross-checking validation between both platforms (or jacks) Steps
  11. ~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
  12. Photo by Fabian Grohs on Unsplash a wild application idea

    appears! develop it for Android and iOS and web.
  13. ~3xthe team the cost the status meetings required the time

    spent on development the time spent on testing the time spent on bug fixing
  14. None
  15. let’s find a solution that works on all platforms.

  16. 2008 2012 2018 2013 2011 2017 2006 2010 2009 2015

    * Kotlin Multiplatform *
  17. - smaller team - typically half of the cost needed

    on native - small learning curve for web developers (advantages) Cross-platform
  18. - 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 (flutter) is not widely used language (for now) - committed to one framework/ language (disadvantages) Cross-platform
  19. how can we have the best of both worlds?

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

  21. kotlin “42.”

  22. None
  23. - 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
  24. Mutability etc. Null safety Default values Type inference String interpolation

    when Collections Easy to learn
  25. Easy to learn - small learning curve involved - easily

    to go from JavaScript/ Swift into Kotlin and back (from a developers’ point of view)
  26. 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
  27. fun greet(name: String, day: String): String { return "Hello $name,

    today is $day." } greet(“Paris”, “Monday”) *updated from http://nilhcem.com/swift-is-like-kotlin/ func greet(_ name: String,_ day: String) -> String { return "Hello \(name), today is \(day)." } greet(“Paris”, “Monday”) Kotlin Swift (functions) Kotlin vs Swift
  28. class Shape { var numberOfSides = 0 func simpleDescription() ->

    String { return "A shape with \(numberOfSides) sides." } } var shape = Shape() shape.numberOfSides = 7 var shapeDescription = shape.simpleDescription() class Shape { var numberOfSides = 0 fun simpleDescription() = "A shape with $numberOfSides sides." } var shape = Shape() shape.numberOfSides = 7 var shapeDescription = shape.simpleDescription() (variables and constants) Kotlin vs Swift Kotlin Swift
  29. kotlin multiplatform “‘Resistance is useless!’ How can anyone maintain a

    positive mental attitude if you’re saying things like that?”
  30. K otlin multiplatform using kotlin in projects that target more

    than one platform
  31. 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
  32. 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
  33. android iOS web desktop model parser network presentation common view

    view view view java/kotlin objective-c/ swift (kotlin) JS supported in jvm
  34. shares application logic doesn’t share the application UI kotlin multiplatform

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

    - you decide what’s worth to share across platforms - interoperability - consistency across platforms - strong community support Kotlin Multiplatform
  36. first reaction

  37. but how can they expect to communicate?

  38. declared at common module expect declared at android module actual

    declared at iOS module declared at …
  39. we want a platform-specific value for name

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

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

    on iOS targets: android and iOS expect object Platform { val name: String }
  42. 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
  43. IntelliJ will ask you declare the implementations for actual tip:

    just press alt+enter
  44. *.kt common expect JVM actual *.kt, *.java, *.jar Native actual

    *.kt, *C, Swift, Framework JS actual *.kt, *.js, NPM
  45. medium.com/@cafonsomota

  46. show me the code!

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

    or so, nothing continued to happen.”
  48. (optional) (or AppCode) IDE’s required

  49. src/ commonMain/ commonTest/ app/ project structure

  50. src/ commonMain/ commonTest/ app/ project structure <your code shared across

    all targets goes here>
  51. androidMain androidTest project structure <your code shared across all targets

    goes here> src/ commonMain/ commonTest/ app/
  52. project structure <your android code here> <your code shared across

    all targets goes here> src/ commonMain/ commonTest/ app/ androidMain androidTest
  53. project structure <your android code here> <your code shared across

    all targets goes here> src/ commonMain/ commonTest/ app/ androidMain androidTest iosMain/ iosTest/ jsMain/ jsTest/ <your JS code goes here> <your platform iOS code here>
  54. github.com/cmota/androidmakers

  55. - user interface - RecyclerView and more (Android) - UITableViewController

    and more (iOS) - multiple network request - parse response objects - store on local database - notify the UI - that there’s new content available to be draw - reload list project structure
  56. android iOS Room CoreData Retrofit Alamofire GSON/ Moshi JSONSerialization MVP,

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

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

    MVVM, ELM RxJava RxSwift Tests Tests Activity UIViewController RecyclerView UITableView android iOS Activity UIViewController RecyclerView UITableView unnecessary duplication
  59. SQLDelight ktor kotlinx.serialization MVP kotlinx.coroutines kotlin.test android iOS Activity UIViewController

    RecyclerView UITableView
  60. multiplatform httpClient ktor SQLDelight android kotlinx.serialization SQLDriver dispatcher view presenter

    presenter model GetSchedule GetSpeakers
  61. multiplatform httpClient ktor SQLDelight iOS model kotlinx.serialization GetSchedule GetSpeakers SQLDriver

    dispatcher view presenter presenter
  62. multiplatform network database android parser presenter presenter view platform domain

    specific model
  63. class SessionizeAPI(engine: HttpClientEngine) { private val client = HttpClient(engine) {

    install(JsonFeature) { serializer = KotlinxSerializer() } } suspend fun fetchSpeakers(): List<SpeakerEntity> { val response = client.get<HttpResponse>{ url ("$URL")} val json = response.readText() return Json.parse(SpeakerEntity.serializer().list, json) } } network src/commonMain/data/SessionizeAPI.kt ktor multiplatform network database android parser view platform specific presenter presenter domain model common (shared) code
  64. kotlinx.serialization network src/commonMain/data/SessionizeAPI.kt class SessionizeAPI(engine: HttpClientEngine) { private val client

    = HttpClient(engine) { install(JsonFeature) { serializer = KotlinxSerializer() } } suspend fun fetchSpeakers(): List<SpeakerEntity> { val response = client.get<HttpResponse>{ url ("$URL")} val json = response.readText() return Json.parse(SpeakerEntity.serializer().list, json) } } multiplatform network database android parser view platform specific presenter presenter domain model
  65. json response [{ "id": "2bc1e95f-1243-4ad0-8a11-75541fee10e0", "firstName": "Carlos", "lastName": "Mota", "fullName":

    "Carlos Mota", "bio": "… GDG Coimbra organizer and Kotlin evangelist, he also has a huge passion for travel, photography, space and the occasional run.", "tagLine": "Lead Software Engineer at WIT Software", "profilePicture": “https://sessionize.com/imag...7ef8707ae.jpg”, "sessions": [{ "id": 131513, "name": "The Hitchhikers Guide Through Kotlin Multiplatform" }], … },… https://sessionize.com/api/v2/3hvwlgcc/view/speakers
  66. json response [{ "id": "2bc1e95f-1243-4ad0-8a11-75541fee10e0", "firstName": "Carlos", "lastName": "Mota", "fullName":

    "Carlos Mota", "bio": "… GDG Coimbra organizer and Kotlin evangelist, he also has a huge passion for travel, photography, space and the occasional run.", "tagLine": "Lead Software Engineer at WIT Software", "profilePicture": “https://sessionize.com/imag...7ef8707ae.jpg”, "sessions": [{ "id": 131513, "name": "The Hitchhikers Guide Through Kotlin Multiplatform" }], … },… https://sessionize.com/api/v2/3hvwlgcc/view/speakers
  67. kotlinx.serialization src/commonMain/data/entities/SpeakerEntity.kt multiplatform network database android parser view platform specific

    presenter presenter domain model parser @Serializable data class SpeakerEntity ( val id: String, val firstName: String, val lastName: String, val fullName: String, val bio: String, val tagLine: String, val profilePicture: String, val sessions: List<SessionShortEntity>, val isTopSpeaker: Boolean, val links: List<String>, val questionAnswers: List<String>, val categories: List<String>)
  68. database CREATE TABLE SpeakerModel ( id TEXT NOT NULL PRIMARY

    KEY, speaker TEXT as Speaker NOT NULL ); insertOrReplaceSpeaker: INSERT OR REPLACE INTO SpeakerModel(id, speaker) VALUES (?, ?); selectAllSpeakers: SELECT * FROM SpeakerModel; SQLDelight src/commonMain/sqldelight/data/model/SpeakerModel.sq multiplatform network database android parser view platform specific presenter presenter domain model
  69. database SQLite SQLDelight multiplatform network database android parser view platform

    specific presenter presenter domain model
  70. database SQLite Compiler Generated code SQLDelight multiplatform network database android

    parser view platform specific presenter presenter domain model
  71. database Generated code SQLDelight multiplatform network database android parser view

    platform specific presenter presenter domain model
  72. app/build/sqldelight/ScheduleDb/data/app/ScheduleDbImpl.kt database class SpeakerModelQueriesImpl(private val db: ScheduleDbImpl, private val driver:

    SqlDriver) : TransacterImpl(driver), SpeakerModelQueries { … override fun insertOrReplaceSpeaker(id: String, speaker: Speaker) { driver.execute(2113668020, """INSERT OR REPLACE INTO SpeakerModel(id, speaker) |VALUES (?1, ?2)""", 2) { bindString(1, id) bindString(2, db.SpeakerModelAdapter.speakerAdapter.encode(speaker)) } notifyQueries(2113668020, {db.speakerModelQueries.selectAllSpeakers}) } } generated class SQLDelight multiplatform network database android parser view platform specific presenter presenter domain model
  73. model class SpeakerDao(database: ScheduleDb) { private val db = database.speakerModelQueries

    internal fun insertOrReplace(speaker: Speaker) { db.insertOrReplaceSpeaker( id = speaker.id, speaker = speaker) } internal fun getAllSpeakers(): List<Speaker> { val data = db.selectAllSpeakers().executeAsList() … src/commonMain/domain/dao/SpeakerDao.kt multiplatform network database android parser view platform specific presenter presenter domain model
  74. model @Serializable data class Speaker ( val id: String, val

    fullName: String, val bio: String, val tagLine: String, val profilePicture: String, val sessions: List<Info>, val categories: List<String>) fun SpeakerEntity.toSpeaker() = Speaker( id = id, fullName = fullName, bio = bio, …) src/commonMain/domain/model/Speaker.kt multiplatform network database android parser view platform specific presenter presenter domain model
  75. domain class GetSpeakers(val api: SessionizeAPI, val dao: SpeakerDao) { suspend

    operator fun invoke(onSuccess: (List<Speaker>) -> Unit, onFailure: (Exception) -> Unit) { try { val result = api.fetchSpeakers() val speakers = Speaker.toSpeaker(result) dao.insertOrReplace(speakers) coroutineScope { onSuccess(speakers) } } catch (e: Exception) { onFailure(e) } src/commonMain/domain/GetSpeakers.kt multiplatform network database android parser view platform specific presenter presenter domain model
  76. domain class GetSpeakers(val api: SessionizeAPI, val dao: SpeakerDao) { suspend

    operator fun invoke(onSuccess: (List<Speaker>) -> Unit, onFailure: (Exception) -> Unit) { try { val result = api.fetchSpeakers() val speakers = Speaker.toSpeaker(result) dao.insertOrReplace(speakers) coroutineScope { onSuccess(speakers) } } catch (e: Exception) { onFailure(e) } src/commonMain/domain/GetSpeakers.kt multiplatform network database android parser view platform specific presenter presenter domain model
  77. domain class GetSpeakers(val api: SessionizeAPI, val dao: SpeakerDao) { suspend

    operator fun invoke(onSuccess: (List<Speaker>) -> Unit, onFailure: (Exception) -> Unit) { try { val result = api.fetchSpeakers() val speakers = Speaker.toSpeaker(result) dao.insertOrReplace(speakers) coroutineScope { onSuccess(speakers) } } catch (e: Exception) { onFailure(e) } src/commonMain/domain/GetSpeakers.kt multiplatform network database android parser view platform specific presenter presenter domain model
  78. domain class GetSpeakers(val api: SessionizeAPI, val dao: SpeakerDao) { suspend

    operator fun invoke(onSuccess: (List<Speaker>) -> Unit, onFailure: (Exception) -> Unit) { try { val result = api.fetchSpeakers() val speakers = Speaker.toSpeaker(result) dao.insertOrReplace(speakers) coroutineScope { onSuccess(speakers) } } catch (e: Exception) { onFailure(e) } src/commonMain/domain/GetSpeakers.kt multiplatform network database android parser view platform specific presenter presenter domain model
  79. class SpeakersListPresenter(val speakers: GetSpeakers, val coroutineContext: CoroutineContext) { lateinit var

    view: ISpeakersData fun attachView(currView: ISpeakersData) { view = currView fetchSpeakersList() } … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/presentation/SpeakersListPresenter.kt
  80. class SpeakersListPresenter(val speakers: GetSpeakers, val coroutineContext: CoroutineContext) { lateinit var

    view: ISpeakersData fun attachView(currView: ISpeakersData) { view = currView fetchSpeakersList() } … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/presentation/SpeakersListPresenter.kt onCreate()/ viewDidLoad()
  81. class SpeakersListPresenter(val speakers: GetSpeakers, val coroutineContext: CoroutineContext) { lateinit var

    view: ISpeakersData fun attachView(currView: ISpeakersData) { view = currView fetchSpeakersList() } … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/presentation/SpeakersListPresenter.kt
  82. presenter multiplatform network database android parser view platform specific presenter

    presenter domain model src/commonMain/presentation/cb/ISpeakersData.kt interface ISpeakersData { fun onSpeakersDataFetched(speakers: List<Speaker>) fun onSpeakersDataFailed(e: Exception) }
  83. class SpeakersListPresenter(val speakers: GetSpeakers, val coroutineContext: CoroutineContext) { lateinit var

    view: ISpeakersData fun attachView(currView: ISpeakersData) { view = currView fetchSpeakersList() } private fun fetchSpeakersList() { PresenterCoroutineScope(coroutineContext).launch { speakers( onSuccess = { view?.onSpeakersDataFetched(it) }, onFailure = { view?.onSpeakersDataFailed(it) }) … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/presentation/SpeakersListPresenter.kt
  84. class SpeakersListPresenter(val speakers: GetSpeakers, val coroutineContext: CoroutineContext) { lateinit var

    view: ISpeakersData fun attachView(currView: ISpeakersData) { view = currView fetchSpeakersList() } private fun fetchSpeakersList() { PresenterCoroutineScope(coroutineContext).launch { speakers( onSuccess = { view?.onSpeakersDataFetched(it) }, onFailure = { view?.onSpeakersDataFailed(it) }) … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/presentation/SpeakersListPresenter.kt
  85. object ServiceLocator { private val sessionizeAPI by lazy { SessionizeAPI(PlatformServiceLocator.httpClientEngine)

    } private val speakerDao by lazy { SpeakerDao(PlatformServiceLocator.databaseEngine) } private val getSpeakers: GetSpeakers get() = GetSpeakers(sessionizeAPI, speakerDao) val getSpeakersPresenter: SpeakersListPresenter get() = SpeakersPresenter(getSpeakers) … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/ServiceLocator.kt
  86. object ServiceLocator { private val sessionizeAPI by lazy { SessionizeAPI(PlatformServiceLocator.httpClientEngine)

    } private val speakerDao by lazy { SpeakerDao(PlatformServiceLocator.databaseEngine) } private val getSpeakers: GetSpeakers get() = GetSpeakers(sessionizeAPI, speakerDao) val getSpeakersPresenter: SpeakersListPresenter get() = SpeakersPresenter(getSpeakers) … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/ServiceLocator.kt called by the UI
  87. object ServiceLocator { private val sessionizeAPI by lazy { SessionizeAPI(PlatformServiceLocator.httpClientEngine)

    } private val speakerDao by lazy { SpeakerDao(PlatformServiceLocator.databaseEngine) } private val getSpeakers: GetSpeakers get() = GetSpeakers(sessionizeAPI, speakerDao) val getSpeakersPresenter: SpeakersListPresenter get() = SpeakersPresenter(getSpeakers) … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/ServiceLocator.kt
  88. object ServiceLocator { private val sessionizeAPI by lazy { SessionizeAPI(PlatformServiceLocator.httpClientEngine)

    } private val speakerDao by lazy { SpeakerDao(PlatformServiceLocator.databaseEngine) } private val getSpeakers: GetSpeakers get() = GetSpeakers(sessionizeAPI, speakerDao) val getSpeakersPresenter: SpeakersListPresenter get() = SpeakersPresenter(getSpeakers) … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/ServiceLocator.kt
  89. object ServiceLocator { private val sessionizeAPI by lazy { SessionizeAPI(PlatformServiceLocator.httpClientEngine)

    } private val speakerDao by lazy { SpeakerDao(PlatformServiceLocator.databaseEngine) } private val getSpeakers: GetSpeakers get() = GetSpeakers(sessionizeAPI, speakerDao) val getSpeakersPresenter: SpeakersListPresenter get() = SpeakersPresenter(getSpeakers) … presenter multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/ServiceLocator.kt
  90. None
  91. platform specific expect object PlatformServiceLocator { val httpClientEngine: HttpClientEngine val

    databaseEngine: ScheduleDb } multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/PlatformServiceLocator.kt
  92. platform specific expect object PlatformServiceLocator { val httpClientEngine: HttpClientEngine val

    databaseEngine: ScheduleDb } multiplatform network database android parser view platform specific presenter presenter domain model src/commonMain/PlatformServiceLocator.kt
  93. platform specific expect object PlatformServiceLocator { val httpClientEngine: HttpClientEngine val

    databaseEngine: ScheduleDb } commonMain
  94. platform specific expect object PlatformServiceLocator { val httpClientEngine: HttpClientEngine val

    databaseEngine: ScheduleDb } commonMain actual object PlatformServiceLocator { actual val httpClientEngine: HttpClientEngine by lazy { OkHttp.create() } actual val databaseEngine: SqlDriver by lazy { AndroidSqliteDriver(ScheduleDb.Schema, ctx, “app.db") … androidMain
  95. platform specific expect object PlatformServiceLocator { val httpClientEngine: HttpClientEngine val

    databaseEngine: ScheduleDb } commonMain actual object PlatformServiceLocator { actual val httpClientEngine: HttpClientEngine by lazy { OkHttp.create() } actual val databaseEngine: SqlDriver by lazy { AndroidSqliteDriver(ScheduleDb.Schema, ctx, “app.db") … androidMain actual object PlatformServiceLocator { actual val httpClientEngine: HttpClientEngine by lazy { Ios.create() } actual val databaseEngine: SqlDriver by lazy { NativeSqliteDriver(ScheduleDb.Schema, “app.db") … iOSMain
  96. platform specific expect object PlatformServiceLocator { val httpClientEngine: HttpClientEngine val

    databaseEngine: ScheduleDb } commonMain actual object PlatformServiceLocator { actual val httpClientEngine: HttpClientEngine by lazy { OkHttp.create() } actual val databaseEngine: SqlDriver by lazy { AndroidSqliteDriver(ScheduleDb.Schema, ctx, “app.db") … androidMain actual object PlatformServiceLocator { actual val httpClientEngine: HttpClientEngine by lazy { Ios.create() } actual val databaseEngine: SqlDriver by lazy { NativeSqliteDriver(ScheduleDb.Schema, “app.db") … iOSMain
  97. platform specific actual object PlatformServiceLocator { actual val httpClientEngine: HttpClientEngine

    by lazy { OkHttp.create() } actual val databaseEngine: SqlDriver by lazy { AndroidSqliteDriver(ScheduleDb.Schema, ctx, “app.db") … androidMain actual object PlatformServiceLocator { actual val httpClientEngine: HttpClientEngine by lazy { Ios.create() } actual val databaseEngine: SqlDriver by lazy { NativeSqliteDriver(ScheduleDb.Schema, “app.db") … iOSMain multiplatform network database android parser view platform specific presenter presenter domain model
  98. src/commonAndroid/presenter/activities/MainActivity.kt android class MainActivity : AppCompatActivity(), ISpeakersData { val presenter

    by lazy { ServiceLocator.getSpeakersPresenter } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) presenter.attachView(this) } override fun onSpeakersDataFetched(list: List<Speaker>){ setup(list) … multiplatform network database android parser view platform specific presenter presenter domain model
  99. src/commonAndroid/presenter/activities/MainActivity.kt android class MainActivity : AppCompatActivity(), ISpeakersData { val presenter

    by lazy { ServiceLocator.getSpeakersPresenter } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) presenter.attachView(this) } override fun onSpeakersDataFetched(list: List<Speaker>){ setup(list) … multiplatform network database android parser view platform specific presenter presenter domain model
  100. src/commonAndroid/presenter/adapters/SpeakersListAdapter.kt android multiplatform network database android parser view platform specific

    presenter presenter domain model override fun onBindViewHolder(viewHolder: SpeakerViewHolder, position: Int) { val speaker = speakers[position] Glide.with(viewHolder.speakerPhoto) .load(speaker.profilePicture) .apply(RequestOptions.circleCropTransform()) .into(viewHolder.speakerPhoto) viewHolder.speakerName.text = speaker.fullName viewHolder.talkTitle.text = speaker.talkTitle viewHolder.container.setOnClickListener { action.onUserClickAction(speaker, it) } }
  101. src/commonAndroid/presenter/adapters/SpeakersListAdapter.kt android override fun onBindViewHolder(viewHolder: SpeakerViewHolder, position: Int) { val

    speaker = speakers[position] Glide.with(viewHolder.speakerPhoto) .load(speaker.profilePicture) .apply(RequestOptions.circleCropTransform()) .into(viewHolder.speakerPhoto) viewHolder.speakerName.text = speaker.fullName viewHolder.talkTitle.text = speaker.talkTitle viewHolder.container.setOnClickListener { action.onUserClickAction(speaker, it) } } you can keep using your android libraries multiplatform network database android parser view platform specific presenter presenter domain model
  102. iosApp/iosApp/SpeakerTableViewController.swift iOS multiplatform network database iOS parser view platform specific

    presenter presenter domain model class SpeakerTableViewController: UITableViewController, ISpeakersData lazy var presenter = ServiceLocator.init().getSpeakerPresenter override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) presenter.attachView(view: self) } func onSpeakersDataFetched(speakers: [Speaker]) { setup(speakers) …
  103. iosApp/iosApp/SpeakerTableViewController.swift iOS multiplatform network database iOS parser view platform specific

    presenter presenter domain model class SpeakerTableViewController: UITableViewController, ISpeakersData { lazy var presenter = ServiceLocator.init().getSpeakersPresenter override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) presenter.attachView(view: self) } func onSpeakersDataFetched(speakers: [Speaker]) { setup(speakers) …
  104. iosApp/iosApp/SpeakerTableViewController.swift iOS multiplatform network database iOS parser view platform specific

    presenter presenter domain model 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.fullName cell.speakerImage.image = speaker.profilePicture return cell }
  105. how much sharing? what makes sense for your app kotlin

    multiplatform
  106. conclusions don’t forget to bring your own towel.

  107. team structure Kotlin Multiplatform android iOS

  108. team structure Kotlin Multiplatform android iOS mobile backend documentation tests

    clean API
  109. (impressions) Kotlin Multiplatform I could just focus on doing what

    I know best - UI. I have no idea how things are being done in the backend, yet I know that when I ask for data I receive it. - iOS Developer
  110. - it’s in experimental state - although we keep seeing

    new projects in production - Space was announced during Kotlin Conf 19 (web, desktop and mobile) (apps in production) Kotlin Multiplatform Space Adapted from: KotlinConf 2019: Opening Keynote by Andrey Breslav Full KMP Cash App Shares: business logic Yandex Maps Shares: business logic, wrappers for C++ libraries PlanGrid Planboard Workspace Shares: business logic, sync logic, mgmnt offline data Shares: business logic, networking, offline caching lyrs Shares: business logic
  111. - start small, don’t try to reach 100% of shared

    logic - perhaps by writing common tests to gain familiarity - one module at a time (network, database, image processing, etc.) - you can spend some time resolving compilation issues - specially if you try to target all platforms (how to start?) Kotlin Multiplatform - it’s in experimental state - experimental means that one simple update can break your builds - lookout for plugins/ libraries updates
  112. - evangelize the iOS team - select one module where

    something is more complex on iOS than on Android - KMP is not intended to share the UI - iOS developers can focus on Swift and frontend features - On cross platform like Flutter or React Native they need to use Dart or JS - not possible to debug kotlin from Xcode - plugin for Xcode from TouchLab/ support mentioned on Kotlin Conf 19 (how to start?) Kotlin Multiplatform
  113. - 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
  114. - Ktor (networking) - Kotlinx.Coroutines - Kotlin.Serialization - SqlDelight (database)

    - Multiplatform Settings - Stately (state management) (libraries) more information
  115. - Kotlinconf App - Droidcon Kotlin - KaMP Kit -

    Sudoku Playground - PeopleInSpace - Kotlin Game Of Life - WorkoutMPP (cool projects to follow) more information
  116. - Kotlin Slack - Kotlin Official - Kotlin by: https://jakewharton.com/presentations/

    https://touchlab.co/kamp-kit-touchlab/ - Kotlin Conf 2019 videos https://www.youtube.com/watch?v=0xKTM0A8gdI - Android Makers Paris Intégrer plusieurs libraires Kotlin Native dans vos apps Android/iOS (April 21st) (useful links) more information
  117. @cafonsomota so long and thanks for all the fish medium.com/@cafonsomota

    github.com/cmota/AndroidMakers