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

Kotlin Multiplatform

Kotlin Multiplatform

Loveleen Kaur

June 30, 2023
Tweet

More Decks by Loveleen Kaur

Other Decks in Programming

Transcript

  1. Meet Kotlin Multiplatform Mobile KMM for short Loveleen Kaur Senior

    Engineer (Astrotalk) Google Android Educator
  2. Who am I? • Loveleen Kaur • Senior Engineer -

    Android @ Astrotalk • Core Team Member @ Google Developer Groups Chandigarh • Co-Organizer @ Kotlin Chandigarh User Group • Android Educator @ Android Educators Community India • Phd Research Scholar • Technical Speaker • Develop Mobile Applications • Happy Android Developer :)
  3. Questions I will try to answer -What is the difference

    between KMM and other multiplatform solutions like a flutter, 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
  4. Why we choose KMM • Small team • Business logic

    in one place • iOS needed refactor
  5. KMM, the PoC • Configuration 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
  6. Networking with KTOR fun createHttpClient(httpClientEngine: HttpClientEngine, logger: Logger) = HttpClient(httpClientEngine)

    { defaultRequest { url.protocol = URLProtocol.HTTPS url.host = BuildKonfig.BASE_URL parameter("apikey", BuildKonfig.API_KEY) parameter("Accept-Language", languageCode ?: "en") } install(ContentNegotiation) { json(json) } install(Logging) { this.logger = logger level = logLevel } }
  7. Networking with KTOR fun createHttpClient(httpClientEngine: HttpClientEngine, logger: Logger) = HttpClient(httpClientEngine)

    { defaultRequest { url.protocol = URLProtocol.HTTPS url.host = BuildKonfig.BASE_URL parameter("apikey", BuildKonfig.API_KEY) parameter("Accept-Language", languageCode ?: "en") } install(ContentNegotiation) { json(json) } install(Logging) { this.logger = logger level = logLevel } } OkHttp + Retrofit Retrofit.Builder().baseUrl() In interceptor: chain.request().header()
  8. fun createHttpClient(httpClientEngine: HttpClientEngine, logger: Logger) = HttpClient(httpClientEngine) { defaultRequest {

    url.protocol = URLProtocol.HTTPS url.host = BuildKonfig.BASE_URL parameter("apikey", BuildKonfig.API_KEY) parameter("Accept-Language", languageCode ?: "en") } install(ContentNegotiation) { json(json) } install(Logging) { this.logger = logger level = logLevel } } OkHttp + Retrofit Retrofit.Builder().baseUrl( ) In interceptor: chain.request().header( ) .addConverterFactory(jsonConverter) Networking with KTOR
  9. fun createHttpClient(httpClientEngine: HttpClientEngine, logger: Logger) = HttpClient(httpClientEngine) { defaultRequest {

    url.protocol = URLProtocol.HTTPS url.host = BuildKonfig.BASE_URL parameter("apikey", BuildKonfig.API_KEY) parameter("Accept-Language", languageCode ?: "en") } install(ContentNegotiation) { json(json) } install(Logging) { this.logger = logger level = logLevel } } OkHttp + Retrofit Retrofit.Builder().baseUrl( ) In interceptor: chain.request().header( ) .addConverterFactory(jsonConverter) .addInterceptor(httpLoggingInterceptor) Networking with KTOR
  10. Http Client Engine Android.create() val logger = object : Logger

    { override fun log(message: String) { Log.i(tag, message) } } Darwin.create() Logger.DEFAULT Logger Networking with KTOR
  11. Actual/Expected //common expect val httpEngine: HttpClientEngine expect val logger: Logger

    //androidMain actual val httpEngine = Android.create() actual val logger = object : Logger { override fun log(message: String) { Log.i(tag, message) } } //iosMain actual val httpEngine = Darwin.create() actual val logger = Logger.DEFAULT
  12. iOS Source: https://dev.to/touchlab/kotlin-coroutines-and-swift-revisited-j5h func createPublisher<T>(flowAdapter: FlowAdapter<T>) -> AnyPublisher<T, KotlinError> {

    return Deferred<Publishers.HandleEvents<PassthroughSubject<T, KotlinError>>> { let subject = PassthroughSubject<T, KotlinError>() 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() }
  13. Shared iOS class FlowAdapter<T : Any>( private val scope: CoroutineScope,

    private val flow: Flow<T> ) { fun subscribe( onEvent: (T) -> Unit, onError: (Throwable) -> Unit, onComplete: () -> Unit ): Job = flow .onEach { onEvent(it) } .catch { onError(it) } .onCompletion { onComplete() } .launchIn(scope) }
  14. 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()
  15. Next steps • Use async/await in iOS for suspend functions

    • Try crashlytics, analytics, etc • Use KMM for web app • Share more assets
  16. KMM - Pros and Cons • 100% Native • Android

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

    developers know Kotlin and Gradle • Optional • Still in Alpha • UI written separately