Slide 1

Slide 1 text

Meet Kotlin Multiplatform Mobile KMM for short Piotr Prus Mobile Tech Lead @Airly GDG 3City Organizer

Slide 2

Slide 2 text

Questions I will try to answer - What is the difference between KMM and other multiplatform solutions like a fl utter, react native, etc? - Is this a good solution for your business and why we thought it is a good decision for us? - How does iOS read the shared module? - How much code can we actually share? - What are the most important libraries and what these are doing? - Is KMM production ready? - Pros and cons of KMM

Slide 3

Slide 3 text

Cross-platform mobile framework popularity Source: www.statista.com, July 2021

Slide 4

Slide 4 text

Cross-platform mobile framework popularity Source: www.statista.com, July 2021

Slide 5

Slide 5 text

Google trends

Slide 6

Slide 6 text

Share code jvm Android iOS Linux macOS Windows JS watchOS tvOS

Slide 7

Slide 7 text

KMM vs KMP First Multiplatform Kotlin 1.2 November 2017 Announced August 2020

Slide 8

Slide 8 text

Why we choose KMM • Small team • Business logic in one place • Promote DDD • iOS needed refactor

Slide 9

Slide 9 text

KMM, the PoC • Con fi guration is tricky, but manageable • Things that works on android, do not need to work on iOS • One repo vs 3 repos • iOS simulator != iOS real device • Sample project != big project 1 week 🎉

Slide 10

Slide 10 text

Networking October 2021

Slide 11

Slide 11 text

Networking January 2022 Persistance Models

Slide 12

Slide 12 text

Networking March 2022 Persistance Models Repositories UseCases

Slide 13

Slide 13 text

Networking June/July 2022 Persistance Models Repositories UseCases ViewModels

Slide 14

Slide 14 text

Networking June/July 2022 Persistance Models Repositories UseCases ViewModels UI layer

Slide 15

Slide 15 text

Share code Domain Repositories Persistance Networking ViewModels Presentation Business(Kotlin) Framework(iOS, Android)

Slide 16

Slide 16 text

KMM libraries 137 libraries KTOR KOIN SQLdelight Multiplatform settings kotlinx.serialization kotlinx.datetime BuildKon fi g Moko-resources

Slide 17

Slide 17 text

Networking with KTOR • open-source • lightweight • powered by coroutines • Build by JetBrains

Slide 18

Slide 18 text

Networking with KTOR fun createHttpClient(httpClientEngine: HttpClientEngine, logger: Logger) = HttpClient(httpClientEngine) { defaultRequest { url.protocol = URLProtocol.HTTP S url.host = BuildKonfig.BASE_UR L parameter("apikey", BuildKonfig.API_KEY ) parameter("Accept-Language", languageCode ?: "en" ) } install(ContentNegotiation) { json(json) } install(Logging) { this.logger = logge r level = logLeve l } }

Slide 19

Slide 19 text

Networking with KTOR fun createHttpClient(httpClientEngine: HttpClientEngine, logger: Logger) = HttpClient(httpClientEngine) { defaultRequest { url.protocol = URLProtocol.HTTP S url.host = BuildKonfig.BASE_UR L parameter("apikey", BuildKonfig.API_KEY ) parameter("Accept-Language", languageCode ?: "en" ) } install(ContentNegotiation) { json(json) } install(Logging) { this.logger = logge r level = logLeve l } } OkHttp + Retro fi t Retro fi t.Builder().baseUrl() In interceptor: chain.request().header()

Slide 20

Slide 20 text

Networking with KTOR fun createHttpClient(httpClientEngine: HttpClientEngine, logger: Logger) = HttpClient(httpClientEngine) { defaultRequest { url.protocol = URLProtocol.HTTP S url.host = BuildKonfig.BASE_UR L parameter("apikey", BuildKonfig.API_KEY ) parameter("Accept-Language", languageCode ?: "en" ) } install(ContentNegotiation) { json(json) } install(Logging) { this.logger = logge r level = logLeve l } } OkHttp + Retro fi t Retro fi t.Builder().baseUrl() In interceptor: chain.request().header() .addConverterFactory(jsonConverter)

Slide 21

Slide 21 text

Networking with KTOR fun createHttpClient(httpClientEngine: HttpClientEngine, logger: Logger) = HttpClient(httpClientEngine) { defaultRequest { url.protocol = URLProtocol.HTTP S url.host = BuildKonfig.BASE_UR L parameter("apikey", BuildKonfig.API_KEY ) parameter("Accept-Language", languageCode ?: "en" ) } install(ContentNegotiation) { json(json) } install(Logging) { this.logger = logge r level = logLeve l } } OkHttp + Retro fi t Retro fi t.Builder().baseUrl() In interceptor: chain.request().header() .addConverterFactory(jsonConverter) .addInterceptor(httpLoggingInterceptor)

Slide 22

Slide 22 text

Http Client Engine Networking with KTOR Android.create() val logger = object : Logger { override fun log(message: String) { Log.i(tag, message) } } Darwin.create() Logger.DEFAULT Logger

Slide 23

Slide 23 text

Actual/Expected

Slide 24

Slide 24 text

Actual/Expected //commo n expect val httpEngine: HttpClientEngin e expect val logger: Logger //androidMai n actual val httpEngine = Android.create( ) actual val logger = object : Logger { override fun log(message: String) { Log.i(tag, message ) } } //iosMai n actual val httpEngine = Darwin.create( ) actual val logger = Logger.DEFAULT

Slide 25

Slide 25 text

iOS and coroutines

Slide 26

Slide 26 text

iOS and coroutines Async/Await RxSwift Combine Callbacks

Slide 27

Slide 27 text

iOS Source: https://dev.to/touchlab/kotlin-coroutines-and-swift-revisited-j5h func createPublisher(flowAdapter: FlowAdapter) -> AnyPublisher { return Deferred>> { let subject = PassthroughSubject( ) let job = flowAdapter.subscribe ( onEvent: { (item) in let _ = subject.send(item) } , onError: { (error) in subject.send(completion: .failure(KotlinError(error))) } , onComplete: { subject.send(completion: .finished) } ) return subject.handleEvents(receiveCancel: { job.cancel(cause: nil ) } ) }.eraseToAnyPublisher( ) }

Slide 28

Slide 28 text

Shared iOS class FlowAdapter ( private val scope: CoroutineScope , private val flow: Flow ) { fun subscribe ( onEvent: (T) -> Unit , onError: (Throwable) -> Unit , onComplete: () -> Uni t ): Job = flo w .onEach { onEvent(it) } .catch { onError(it) } .onCompletion { onComplete() } .launchIn(scope ) }

Slide 29

Slide 29 text

SSOT for resources

Slide 30

Slide 30 text

Resources Moko-resources https://github.com/icerockdev/moko-resources

Slide 31

Slide 31 text

Strings.xml Resources Cancel Current location Yesterday

Slide 32

Slide 32 text

Compose Resources stringResource(id = SharedRes.strings.my_string.resourceId) Swift/SwiftUI let string = MR.strings().my_string.desc().localized( ) LocalizedStringKey(MR.strings().email.resourceId)

Slide 33

Slide 33 text

Resources Common val stringDesc = StringDesc.Resource(SharedRes.strings.airlypedia_title)

Slide 34

Slide 34 text

Resources Common val stringDesc = StringDesc.Resource(SharedRes.strings.airlypedia_title) .toString(context)

Slide 35

Slide 35 text

Resources Common val stringDesc = StringDesc.Resource(SharedRes.strings.airlypedia_title) .toString(context) .localized()

Slide 36

Slide 36 text

Resources Common val stringDesc = StringDesc.Resource(SharedRes.strings.airlypedia_title ) expect fun ResourceStringDesc.getText(): String

Slide 37

Slide 37 text

Resources Common val stringDesc = StringDesc.Resource(SharedRes.strings.airlypedia_title ) expect fun ResourceStringDesc.getText(): String actual fun ResourceStringDesc.getText(): String = this.toString(appContext )

Slide 38

Slide 38 text

Resources Common val stringDesc = StringDesc.Resource(SharedRes.strings.airlypedia_title ) expect fun ResourceStringDesc.getText(): String actual fun ResourceStringDesc.getText(): String = this.toString(appContext ) actual fun ResourceStringDesc.getText(): String = this.localized()

Slide 39

Slide 39 text

How to read KMM in platforms?

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

dependencies { implementation(project(":shared") ) }

Slide 42

Slide 42 text

dependencies { implementation(project(":shared") ) }

Slide 43

Slide 43 text

dependencies { implementation(project(":shared") ) } New Run Script Phase

Slide 44

Slide 44 text

dependencies { implementation(project(":shared") ) } New Run Script Phase https://kotlinlang.org/docs

Slide 45

Slide 45 text

Next steps • Use async/await in iOS for suspend functions • Try crashlytics, analytics, etc • Use KMM for web app • Share more assets

Slide 46

Slide 46 text

iOS & Android everyday

Slide 47

Slide 47 text

iOS & Android using KMM

Slide 48

Slide 48 text

Is it ready for production?

Slide 49

Slide 49 text

KMM - Pros and Cons • 100% Native • Android developers know 
 Kotlin and Gradle • Optional • Still in Alpha

Slide 50

Slide 50 text

KMM - Pros and Cons • 100% Native • Android developers know 
 Kotlin and Gradle • Optional • Still in Alpha • UI written separately

Slide 51

Slide 51 text

Meet Kotlin Multiplatform Mobile Thanks for listening! Piotr Prus Mobile Tech Lead @Airly GDG 3City Organizer

Slide 52

Slide 52 text

Meet Kotlin Multiplatform Mobile Questions? Piotr Prus Mobile Tech Lead @Airly GDG 3City Organizer