Slide 1

Slide 1 text

Android Makers: Virtual Edition The Hitchhikers Guide Through Kotlin Multiplatform @cafonsomota

Slide 2

Slide 2 text

hello . @cafonsomota

Slide 3

Slide 3 text

Questions? bit.ly/kmp-androidmakers @cafonsomota

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

android iOS

Slide 8

Slide 8 text

Photo by Fabian Grohs on Unsplash

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

-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

Slide 11

Slide 11 text

~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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

~3xthe team the cost the status meetings required the time spent on development the time spent on testing the time spent on bug fixing

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

- 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

Slide 19

Slide 19 text

how can we have the best of both worlds?

Slide 20

Slide 20 text

how can we have the best of both worlds?

Slide 21

Slide 21 text

kotlin “42.”

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

- 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

Slide 24

Slide 24 text

Mutability etc. Null safety Default values Type inference String interpolation when Collections Easy to learn

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

shares application logic doesn’t share the application UI kotlin multiplatform

Slide 35

Slide 35 text

(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

Slide 36

Slide 36 text

first reaction

Slide 37

Slide 37 text

but how can they expect to communicate?

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

we want a platform-specific value for name

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

commonMain src/commonMain/sample/Platform.kt - define actual on android - define actual on iOS targets: android and iOS expect object Platform { val name: String }

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

IntelliJ will ask you declare the implementations for actual tip: just press alt+enter

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

medium.com/@cafonsomota

Slide 46

Slide 46 text

show me the code!

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

(optional) (or AppCode) IDE’s required

Slide 49

Slide 49 text

src/ commonMain/ commonTest/ app/ project structure

Slide 50

Slide 50 text

src/ commonMain/ commonTest/ app/ project structure

Slide 51

Slide 51 text

androidMain androidTest project structure src/ commonMain/ commonTest/ app/

Slide 52

Slide 52 text

project structure src/ commonMain/ commonTest/ app/ androidMain androidTest

Slide 53

Slide 53 text

project structure src/ commonMain/ commonTest/ app/ androidMain androidTest iosMain/ iosTest/ jsMain/ jsTest/

Slide 54

Slide 54 text

github.com/cmota/androidmakers

Slide 55

Slide 55 text

- 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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

SQLDelight ktor kotlinx.serialization MVP kotlinx.coroutines kotlin.test android iOS Activity UIViewController RecyclerView UITableView

Slide 60

Slide 60 text

multiplatform httpClient ktor SQLDelight android kotlinx.serialization SQLDriver dispatcher view presenter presenter model GetSchedule GetSpeakers

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

multiplatform network database android parser presenter presenter view platform domain specific model

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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 { val response = client.get{ 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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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, val isTopSpeaker: Boolean, val links: List, val questionAnswers: List, val categories: List)

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

database SQLite SQLDelight multiplatform network database android parser view platform specific presenter presenter domain model

Slide 70

Slide 70 text

database SQLite Compiler Generated code SQLDelight multiplatform network database android parser view platform specific presenter presenter domain model

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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 { val data = db.selectAllSpeakers().executeAsList() … src/commonMain/domain/dao/SpeakerDao.kt multiplatform network database android parser view platform specific presenter presenter domain model

Slide 74

Slide 74 text

model @Serializable data class Speaker ( val id: String, val fullName: String, val bio: String, val tagLine: String, val profilePicture: String, val sessions: List, val categories: List) 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

Slide 75

Slide 75 text

domain class GetSpeakers(val api: SessionizeAPI, val dao: SpeakerDao) { suspend operator fun invoke(onSuccess: (List) -> 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

Slide 76

Slide 76 text

domain class GetSpeakers(val api: SessionizeAPI, val dao: SpeakerDao) { suspend operator fun invoke(onSuccess: (List) -> 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

Slide 77

Slide 77 text

domain class GetSpeakers(val api: SessionizeAPI, val dao: SpeakerDao) { suspend operator fun invoke(onSuccess: (List) -> 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

Slide 78

Slide 78 text

domain class GetSpeakers(val api: SessionizeAPI, val dao: SpeakerDao) { suspend operator fun invoke(onSuccess: (List) -> 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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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) fun onSpeakersDataFailed(e: Exception) }

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

No content

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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){ setup(list) … multiplatform network database android parser view platform specific presenter presenter domain model

Slide 99

Slide 99 text

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){ setup(list) … multiplatform network database android parser view platform specific presenter presenter domain model

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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 }

Slide 105

Slide 105 text

how much sharing? what makes sense for your app kotlin multiplatform

Slide 106

Slide 106 text

conclusions don’t forget to bring your own towel.

Slide 107

Slide 107 text

team structure Kotlin Multiplatform android iOS

Slide 108

Slide 108 text

team structure Kotlin Multiplatform android iOS mobile backend documentation tests clean API

Slide 109

Slide 109 text

(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

Slide 110

Slide 110 text

- 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

Slide 111

Slide 111 text

- 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

Slide 112

Slide 112 text

- 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

Slide 113

Slide 113 text

- 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

Slide 114

Slide 114 text

- Ktor (networking) - Kotlinx.Coroutines - Kotlin.Serialization - SqlDelight (database) - Multiplatform Settings - Stately (state management) (libraries) more information

Slide 115

Slide 115 text

- Kotlinconf App - Droidcon Kotlin - KaMP Kit - Sudoku Playground - PeopleInSpace - Kotlin Game Of Life - WorkoutMPP (cool projects to follow) more information

Slide 116

Slide 116 text

- 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

Slide 117

Slide 117 text

@cafonsomota so long and thanks for all the fish medium.com/@cafonsomota github.com/cmota/AndroidMakers