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

Architectures with Kotlin Multiplatform

Ce1ca64f3265f01a8718a622427f0a1d?s=47 Jitin
March 30, 2019

Architectures with Kotlin Multiplatform

In the current wave of cross platform solutions, Kotlin’s multiplatform is shaping up to be a unique proponent of code reuse. Unlike write once run everywhere, Multiplatform offers a unique space of reusability and formalization of code across platforms and also removes dependency on custom VMs or runtime. Modern apps are build on top of abstractions and architectures which make sure code-bases remain mature, testable and error free. They on the other hand also introduce a lot of boilerplate code. This presents an opportunity of code reuse with help of Kotlin multiplatform. Through this talk, we’ll see how can harness power of Kotlin Multiplatform to create apps using architectures such as MVP, MVI etc and use Kotlin native to build an abstraction layer across platforms. We’ll also go through usage of various multiplatform libraries for database and network management.



March 30, 2019

More Decks by Jitin

Other Decks in Technology


  1. Architectures with Kotlin Multiplatform Jitin Sharma GO-JEK @_jitinsharma

  2. Most popular among Android Developers Was designed with platform agnostic

    behaviour. Has a lot of features which can targeted outside JVM environment.
  3. JVM/Server Android .kts Javascript Native

  4. Kotlin Native Can target iOS, Windows, MacOS, Linux etc No

    Runtime environment
  5. Interop C/Objective C interop provided System frameworks are automatically imported

    in case of iOS. Types are mapped with Swift/Objective C.
  6. Multiplatform One Language to rule them all Started with Kotlin

    1.2 Still in experimental phase. Built in tooling with IntellijIDEA.
  7. “Multiplatform is not cross platform” –Kotlin

  8. common android iOS js server project - Presentation logic -

    ui-logic - Data models - Helper/utilities - Networking - Storage - Data models - Networking? - Storage?
  9. onStart onResume … viewDidLoad viewDidAppear … Async networking Async storage

    Kotlin Swift
  10. app commonMain main iosMain iosApp .kt,xml .kt .kt Imports common

    libraries Imports iOS libraries Builds Android app Builds .framework .swift, xib
  11. None
  12. Common Logic dependencies { implementation kotlin('stdlib-common') }

  13. val names = mutableListOf("Bumblebee", "Optimus Prime", "Megatron") names.sort() Foo.kt

  14. expect fun <T : Comparable<T>> MutableList<T>.sort(): Unit val names =

    mutableListOf("Bumblebee", "Optimus Prime", "Megatron") names.sort() public actual fun <T : Comparable<T>> MutableList<T>.sort(): Unit { if (size > 1) java.util.Collections.sort(this) } MutableCollectionsJVM.kt CollectionsH.kt Foo.kt
  15. expect & actual expect fun … expect val… expect class…

    Actual Platform declarations
  16. Client Server

  17. NetworkClient.kt class NetworkClient { private val client = HttpClient() fun

    get(url: String, callback: (String) -> Unit) { GlobalScope.launch(ApplicationDispatcher) { val result: String = client.get { url(url) } callback(result) } } } internal expect val ApplicationDispatcher: CoroutineDispatcher
  18. internal expect val ApplicationDispatcher: CoroutineDispatcher internal actual val ApplicationDispatcher: CoroutineDispatcher

    = Dispatchers.Default AndroidDispatcher.kt
  19. internal expect val ApplicationDispatcher: CoroutineDispatcher import platform.darwin.dispatch_async import platform.darwin.dispatch_get_main_queue import

    platform.darwin.dispatch_queue_t internal actual val ApplicationDispatcher: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue() internal class NsQueueDispatcher(private val dispatchQueue: dispatch_queue_t) : CoroutineDispatcher() { override fun dispatch(context: CoroutineContext, block: Runnable) { dispatch_async(dispatchQueue) { block.run() } } } IOSDispatcher.kt
  20. Serialization @Serializable data class AllData( val avatar_url: String?, val bio:

    String?, val blog: String, .... ) import kotlinx.serialization.*
  21. Persistence - SQLDelight CREATE TABLE Photos ( id INTEGER NOT

    NULL, title TEXT NOT NULL ); insert: INSERT INTO Photos (id, title) VALUES (?, ?); getAll: SELECT * FROM Photos;
  22. Persistence expect val db: Database db.photoQueries.getAll() -> List<Photo> //iOS actual

    val db = Database(NativeSqliteDriver(Database.Schema, "photo.db")) //Android actual val db = Database(AndroidSqliteDriver(Database.Schema, context, “photo.db"))
  23. None
  24. None
  25. interface MainView { fun displayData(data: DisplayData) fun showLoader() fun hideLoader()

  26. class DataRepository { private val api = NetworkApi("https://github.com") fun getData(username:

    String, callback : (Data) -> Unit) { val data = api.getAll(username) { callback(it) } } }
  27. class MainPresenter(private val view: MainView, private val repository: DataRepository) {

    fun loadData(userName: String) { if (userName.isNullOrEmpty()) { view.showError(USER_NAME_NOT_VALID) } else { view.showLoader() repository.getData(userName) { showData() view.hideLoader() } } } }
  28. extension ViewController: MainView { func showLoader() { activityIndicator.startAnimating() } func

    hideLoader() { activityIndicator.stopAnimating() } func displayData(data: DisplayData) { .... } } ViewController.swift
  29. PhotoListState

  30. PhotoListState Store States Reducers Middlewares Action

  31. data class Photo( @SerializedName("id") val id: String, @SerializedName("title") val title:

    String, ...... ) gson serialization
  32. expect val store: Store<AppState> expect class AppState : StateType expect

    fun appReducer(action: Action, state: AppState?): AppState class LoadRecentPhotos : Action class SerializeResponse(val response: String) : Action Actions.kt Common
  33. actual val store: Store<AppState> = Store( state = AppState(), reducer

    = ::appReducer, middleware = listOf(networkMiddleWare, databaseMiddleWare) ) actual data class AppState(val photoListState: PhotoListState? = null) : StateType actual fun appReducer(action: Action, state: AppState?): AppState { return AppState(photoListState = photoListReducer(action, state?.photoListState)) } Android
  34. class MainActivity : AppCompatActivity(), StoreSubscriber<PhotoListState?> { override fun newState(state: PhotoListState?)

    { state.shouldUpdate { val photos = it.photos setImageAdapter(photos) } } }
  35. Performance Generated framework binaries are smaller in size. Few extra

    classes are generated for various kotlin types For Android/JVM there is not much impact.
  36. Debugging For Android/JVM projects, both common code and android code

    can be debugged right from IDE. For iOS, generated framework files can be debugged accordingly.
  37. Testing Shared code test cases can be written out of

    IntelliJ using kotlin.test Platform specific execution using platform test libraries. Existing platform libraries(espresso, xctest) can still be used.
  38. DIY Multiplatform Libraries common android iOS ./gradlew build library.jar library.framework

    Multiplatform Publishing library-common.jar library-android.jar library-ios.klib .module
  39. DIY Multiplatform Libraries common android iOS ./gradlew build library.jar library.framework

    Multiplatform Publishing library-common.jar library-android.jar library-ios.klib .module maven, pod
  40. What’s NOT great Dependence of Multiplatform libs for better code

    sharing Still experimental
  41. What’s great Language - Kotlin No external dependencies as in

    custom runtime/ VM Pragmatic APIs Platform specific libs with common Kotlin code
  42. Multiplatform Libraries https://github.com/ktorio/ktor https://github.com/Kotlin/kotlinx.coroutines https://github.com/square/sqldelight/ https://github.com/russhwolf/multiplatform-settings https://github.com/ReKotlin/rekotlin-multiplatform

  43. Thanks @_jitinsharma