Slide 1

Slide 1 text

Architectures with Kotlin Multiplatform Jitin Sharma GO-JEK @_jitinsharma

Slide 2

Slide 2 text

Most popular among Android Developers Was designed with platform agnostic behaviour. Has a lot of features which can targeted outside JVM environment.

Slide 3

Slide 3 text

JVM/Server Android .kts Javascript Native

Slide 4

Slide 4 text

Kotlin Native Can target iOS, Windows, MacOS, Linux etc No Runtime environment

Slide 5

Slide 5 text

Interop C/Objective C interop provided System frameworks are automatically imported in case of iOS. Types are mapped with Swift/Objective C.

Slide 6

Slide 6 text

Multiplatform One Language to rule them all Started with Kotlin 1.2 Still in experimental phase. Built in tooling with IntellijIDEA.

Slide 7

Slide 7 text

“Multiplatform is not cross platform” –Kotlin

Slide 8

Slide 8 text

common android iOS js server project - Presentation logic - ui-logic - Data models - Helper/utilities - Networking - Storage - Data models - Networking? - Storage?

Slide 9

Slide 9 text

onStart onResume … viewDidLoad viewDidAppear … Async networking Async storage Kotlin Swift

Slide 10

Slide 10 text

app commonMain main iosMain iosApp .kt,xml .kt .kt Imports common libraries Imports iOS libraries Builds Android app Builds .framework .swift, xib

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Common Logic dependencies { implementation kotlin('stdlib-common') }

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

expect & actual expect fun … expect val… expect class… Actual Platform declarations

Slide 16

Slide 16 text

Client Server

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

internal expect val ApplicationDispatcher: CoroutineDispatcher internal actual val ApplicationDispatcher: CoroutineDispatcher = Dispatchers.Default AndroidDispatcher.kt

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Serialization @Serializable data class AllData( val avatar_url: String?, val bio: String?, val blog: String, .... ) import kotlinx.serialization.*

Slide 21

Slide 21 text

Persistence - SQLDelight CREATE TABLE Photos ( id INTEGER NOT NULL, title TEXT NOT NULL ); insert: INSERT INTO Photos (id, title) VALUES (?, ?); getAll: SELECT * FROM Photos;

Slide 22

Slide 22 text

Persistence expect val db: Database db.photoQueries.getAll() -> List //iOS actual val db = Database(NativeSqliteDriver(Database.Schema, "photo.db")) //Android actual val db = Database(AndroidSqliteDriver(Database.Schema, context, “photo.db"))

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

interface MainView { fun displayData(data: DisplayData) fun showLoader() fun hideLoader() }

Slide 26

Slide 26 text

class DataRepository { private val api = NetworkApi("https://github.com") fun getData(username: String, callback : (Data) -> Unit) { val data = api.getAll(username) { callback(it) } } }

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

extension ViewController: MainView { func showLoader() { activityIndicator.startAnimating() } func hideLoader() { activityIndicator.stopAnimating() } func displayData(data: DisplayData) { .... } } ViewController.swift

Slide 29

Slide 29 text

PhotoListState

Slide 30

Slide 30 text

PhotoListState Store States Reducers Middlewares Action

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

expect val store: Store expect class AppState : StateType expect fun appReducer(action: Action, state: AppState?): AppState class LoadRecentPhotos : Action class SerializeResponse(val response: String) : Action Actions.kt Common

Slide 33

Slide 33 text

actual val store: Store = 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

Slide 34

Slide 34 text

class MainActivity : AppCompatActivity(), StoreSubscriber { override fun newState(state: PhotoListState?) { state.shouldUpdate { val photos = it.photos setImageAdapter(photos) } } }

Slide 35

Slide 35 text

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.

Slide 36

Slide 36 text

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.

Slide 37

Slide 37 text

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.

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

What’s NOT great Dependence of Multiplatform libs for better code sharing Still experimental

Slide 41

Slide 41 text

What’s great Language - Kotlin No external dependencies as in custom runtime/ VM Pragmatic APIs Platform specific libs with common Kotlin code

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

Thanks @_jitinsharma