Slide 1

Slide 1 text

Kotlin Multiplatform for Android & iOS Mohit Sarveiya

Slide 2

Slide 2 text

Kotlin Multiplatform for Android & iOS • Why Kotlin Multiplatform? • Project Structure • Setting up project for Android & iOS • Sharing Presenters and Models in MVP • Objective-C/Swift interop with Kotlin

Slide 3

Slide 3 text

• Why Kotlin Multiplatform? • Project Structure • Setting up project for Android & iOS • Sharing Presenters and Models in MVP • Objective-C/Swift interop with Kotlin Kotlin Multiplatform for Android & iOS

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Architectures • Android • Model View Presenter • iOS • Model View ViewModel (ish)

Slide 9

Slide 9 text

Model View Presenter (Android) View Presenter API Analytics Validation Models

Slide 10

Slide 10 text

Model View ViewModel (iOS) View API Analytics Validation Models ViewModel

Slide 11

Slide 11 text

Duplicate Business Logic • API Requests • Data Transfer Objects • Tracking Analytics • Title and Description Validation Logic

Slide 12

Slide 12 text

Cross Platform Frameworks Xamarin React Native Flutter

Slide 13

Slide 13 text

https://flutter.io/

Slide 14

Slide 14 text

Cross platform frameworks Benefits • Good for building new apps • Write once for both Android and iOS Drawbacks • No interop with existing languages and frameworks • Hard to convert large native apps

Slide 15

Slide 15 text

Kotlin Multiplatform Server Kotlin/JVM Browser Kotlin/JS Android Kotlin/JVM iOS Kotlin/LLVM

Slide 16

Slide 16 text

Sharing code between platforms API Analytics Validation

Slide 17

Slide 17 text

• Kotlin Multiplatform Structure • Why Kotlin Multiplatform? • Kotlin Multiplatform Structure • Setting up project for Android & iOS • Sharing Presenters and Models in MVP • Objective-C/Swift interop with Kotlin Kotlin Multiplatform for Android & iOS

Slide 18

Slide 18 text

Multiplatform Structure 3 types of modules • Common Module • Platform Module (JVM & iOS) • Regular Module (Android & iOS app)

Slide 19

Slide 19 text

Multiplatform Structure Common 
 module JVM
 module iOS
 module

Slide 20

Slide 20 text

Common Module • Kotlin only code • Platform independent declarations using expect keyword • Kotlin Standard Common Library (kotlin-stdlib-common)

Slide 21

Slide 21 text

Platform Independent Declarations expect class Platform() { val platform: String }

Slide 22

Slide 22 text

Platform Independent Declarations class Platform() { val platform: String } expect

Slide 23

Slide 23 text

What can expect be applied to? • Classes • Functions • Annotations

Slide 24

Slide 24 text

Platform Independent Declarations expect class Platform() { val platform: String } class Greeting { fun greeting(): String = "Hello, ${Platform().platform}" }

Slide 25

Slide 25 text

Platform Module • Kotlin only code • Platform implementations using actual keyword • Interop with Objective-C libraries (iOS)

Slide 26

Slide 26 text

Platform Specific Declaration (JVM) expect class Platform() { val platform: String }

Slide 27

Slide 27 text

Platform Specific Declaration (JVM) actual class Platform actual constructor() { actual val platform: String = “Android" } expect class Platform() { val platform: String }

Slide 28

Slide 28 text

Platform Specific Declaration (iOS) actual class Platform actual constructor() { actual val platform: String = “iOS” } expect class Platform() { val platform: String }

Slide 29

Slide 29 text

Multiplatform Structure Common 
 module JVM
 module

Slide 30

Slide 30 text

Compile Android App Common 
 module JVM
 module

Slide 31

Slide 31 text

Compile Android App Kotlin 
 Compiler Common 
 module JVM
 module

Slide 32

Slide 32 text

Compile Android App Kotlin 
 Compiler Library android.jar Common 
 module JVM
 module

Slide 33

Slide 33 text

Compile Android App Kotlin 
 Compiler Library android.jar Common 
 module JVM
 module

Slide 34

Slide 34 text

Compile Android App class Greeting { fun greeting(): String = “Hello, ${Platform().platform}" } public final class Greeting public constructor() { public final fun greeting(): kotlin.String { !/* compiled code !*/ } } android.jar

Slide 35

Slide 35 text

Running Android App Greeting().greeting() !// Hello, Android

Slide 36

Slide 36 text

Multiplatform Structure Common 
 module iOS
 module Framework

Slide 37

Slide 37 text

Compile iOS App Common 
 module iOS 
 module Kotlin Native
 Compiler

Slide 38

Slide 38 text

Compile iOS App Common 
 module iOS 
 module Kotlin Native
 Compiler Framework Objective-C

Slide 39

Slide 39 text

Compile iOS App Common 
 module iOS 
 module Kotlin Native
 Compiler Framework Objective-C

Slide 40

Slide 40 text

Compile iOS App class Greeting { fun greeting(): String = “Hello, ${Platform().platform}" }

Slide 41

Slide 41 text

Compile iOS App !__attribute!__((objc_subclassing_restricted)) @interface SharedGreeting : KotlinBase -(instancetype)init NS_SWIFT_NAME(init()) NS_DESIGNATED_INITIALIZER; -(NSString *)greeting NS_SWIFT_NAME(greeting()); @end;

Slide 42

Slide 42 text

Compile iOS App SharedGreeting.init().greeting() !// Hello, iOS

Slide 43

Slide 43 text

• Setting up project for Android & iOS • Why Kotlin Multiplatform? • Project Structure • Setting up project for Android & iOS • Sharing Presenters and Models in MVP • Objective-C/Swift interop with Kotlin Kotlin Multiplatform for Android & iOS

Slide 44

Slide 44 text

IDE(s) Intellij IDEA CLion Android Studio XCode

Slide 45

Slide 45 text

Setting up Kotlin multiplatform project 1. Common Module
 2. JVM Platform Module
 3. iOS Platform Module
 4. Android App
 5. iOS App

Slide 46

Slide 46 text

Setup Common Module Common 
 module JVM
 module iOS
 module

Slide 47

Slide 47 text

Directory Structure Shared build.gradle Common build.gradle JVM iOS build.gradle build.gradle

Slide 48

Slide 48 text

Directory Structure Shared build.gradle Common build.gradle JVM iOS build.gradle build.gradle

Slide 49

Slide 49 text

Common Module apply plugin: 'kotlin-platform-common' group = 'com.learn.shared' version = 1.0 dependencies { !// Set up compilation dependency on common Kotlin stdlib compile “org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version" }

Slide 50

Slide 50 text

Directory Structure Shared build.gradle Common build.gradle JVM iOS build.gradle build.gradle

Slide 51

Slide 51 text

JVM Platform Module apply plugin: 'kotlin-platform-jvm' group = 'com.learn.shared' version = 1.0 dependencies { !// Specify Kotlin/JVM stdlib dependency. implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" !// Specify dependency on a common project for Kotlin multiplatform build. expectedBy project(':shared:common') }

Slide 52

Slide 52 text

Directory Structure Shared build.gradle Common build.gradle JVM iOS build.gradle build.gradle

Slide 53

Slide 53 text

iOS Platform Module apply plugin: 'konan' !// Specify targets to build the framework: iOS and iOS simulator konan.targets = ['ios_arm64', 'ios_x64'] konanArtifacts { !// Declare building into a framework. framework('Shared') { !// The multiplatform support is disabled by default. enableMultiplatform true } } dependencies { !// Specify dependency on a common project for Kotlin multiplatform build expectedBy project(':shared:common') }

Slide 54

Slide 54 text

iOS Platform Module apply plugin: 'konan' !// Specify targets to build the framework: iOS and iOS simulator konan.targets = ['ios_arm64', 'ios_x64'] konanArtifacts { !// Declare building into a framework. framework('Shared') { !// The multiplatform support is disabled by default. enableMultiplatform true } } dependencies { !// Specify dependency on a common project for Kotlin multiplatform build expectedBy project(':shared:common') } apply plugin: 'konan'

Slide 55

Slide 55 text

iOS Platform Module apply plugin: 'konan' !// Specify targets to build the framework: iOS and iOS simulator konan.targets = ['ios_arm64', 'ios_x64'] konanArtifacts { !// Declare building into a framework. framework('Shared') { !// The multiplatform support is disabled by default. enableMultiplatform true } } dependencies { !// Specify dependency on a common project for Kotlin multiplatform build expectedBy project(':shared:common') } !// Specify targets to build the framework: iOS and iOS simulator konan.targets = ['ios_arm64', 'ios_x64']

Slide 56

Slide 56 text

Konan Targets • android_arm32 • android_arm64 • ios_x64 • linux_x64 • mingw_x64 • macos_x64 • linux_arm32_hfp • linux_mips32 • linux_mipsel32 • wasm32

Slide 57

Slide 57 text

konanArtifacts { !// Declare building into a framework. framework('Shared') { !// The multiplatform support is disabled by default. enableMultiplatform true } } iOS Platform Module apply plugin: 'konan' !// Specify targets to build the framework: iOS and iOS simulator konan.targets = ['ios_arm64', 'ios_x64'] konanArtifacts { !// Declare building into a framework. framework('Shared') { !// The multiplatform support is disabled by default. enableMultiplatform true } } dependencies { !// Specify dependency on a common project for Kotlin multiplatform build expectedBy project(':shared:common') }

Slide 58

Slide 58 text

Kotlin Artifacts • program • library • bitcode • interop • framework

Slide 59

Slide 59 text

dependencies { !// Specify dependency on a common project for Kotlin multiplatform build expectedBy project(':shared:common') } iOS Platform Module apply plugin: 'konan' !// Specify targets to build the framework: iOS and iOS simulator konan.targets = ['ios_arm64', 'ios_x64'] konanArtifacts { !// Declare building into a framework. framework('Shared') { !// The multiplatform support is disabled by default. enableMultiplatform true } } dependencies { !// Specify dependency on a common project for Kotlin multiplatform build expectedBy project(':shared:common') }

Slide 60

Slide 60 text

Shared Code Setup Complete Common 
 module JVM
 module iOS
 module

Slide 61

Slide 61 text

Setup App Modules Common 
 module JVM
 module iOS
 module iOS
 App Android 
 App

Slide 62

Slide 62 text

apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { … } dependencies { … implementation ‘com.learn.shared:jvm:1.0' … } Android App

Slide 63

Slide 63 text

iOS App 1. Configure build settings 2. Configure build phase

Slide 64

Slide 64 text

iOS Build Settings

Slide 65

Slide 65 text

iOS Build Phase

Slide 66

Slide 66 text

Complete Setup Common 
 module JVM
 module iOS
 module Android 
 App iOS
 App

Slide 67

Slide 67 text

• Sharing Presenters and Models in MVP Kotlin Multiplatform for Android & iOS • Why Kotlin Multiplatform? • Project Structure • Setting up project for Android & iOS • Sharing Presenters and Models in MVP • Objective-C/Swift interop with Kotlin

Slide 68

Slide 68 text

Multiplatform App in MVP

Slide 69

Slide 69 text

Multiplatform App in MVP • Get videos from API • Track video selection to Localytics • Play the video

Slide 70

Slide 70 text

API GET /videos { "videos": [ { "id": 1, 
 “title": "Big Buck Bunny", "desc": "The Big Buck Bunny video.", "url": “https:!//clips.mohit.com/big_buck_bunny.mp4” }, { "id": 2, "title": "Timelapse Video of Sky", "desc": "Awesome timelapse video.", "url": “https:!//clips.mohit.com/timelapse_video_of_sky.mp4” }
 ] }

Slide 71

Slide 71 text

Common module data class Video(val id: Int, val title: String, val desc: String, val url: String )

Slide 72

Slide 72 text

Limitation: Common HTTP Library Common 
 HTTP Library Android
 App iOS
 App ❌

Slide 73

Slide 73 text

Alternatives 1. Build common HTTP client for JVM and Native
 2. Use Dependency Injection to inject API requests
 3. Use NSURLConnection in iOS and Retrofit in Android

Slide 74

Slide 74 text

Common HTTP Library

Slide 75

Slide 75 text

JVM & iOS Platform Modules runSuspend { HttpClient().request { with(url) { protocol = URLProtocol.HTTPS host = endpoint port = 443 } } }.then { response !-> println(response.body) client.close() }

Slide 76

Slide 76 text

Alternative: Using DI to inject API Behavior Common
 module Retrofit Alamofire

Slide 77

Slide 77 text

Common module interface VideosInteractor { fun fetchVideos(callback: FetchDataCallback>) }

Slide 78

Slide 78 text

Generated Objective-C for interface @protocol SharedVideosInteractor @required -(void)fetchVideosUri:
 callback:(id)callback 
 NS_SWIFT_NAME(fetchVideos(callback:)); @end;

Slide 79

Slide 79 text

Mappings https://github.com/JetBrains/kotlin-native/blob/master/OBJC_INTEROP.md

Slide 80

Slide 80 text

iOS App class VideosInteractorImpl : NSObject, SharedVideosInteractor { func fetchVideos(callback: SharedFetchDataCallback) { Alamofire.request(uri).responseJSON { response in if let json = response.result.value { let videos = self.parseResponse(response: json) callback.onFetchedData(data: videos) } else { callback.onError() } } }

Slide 81

Slide 81 text

Android App class VideosInteractorImpl : VideosInteractor { override fun fetchVideos(fetchedCallback: FetchDataCallback>) { RetrofitFactory.create().videos().enqueue(object: Callback> { override fun onResponse(call: Call>?, response: Response> val videos = response!?.body() if (videos !!= null) { fetchedCallback.onFetchedData(videos) } else { fetchedCallback.onError() } } override fun onFailure(call: Call>?, t: Throwable?) { fetchedCallback.onError() } }) }

Slide 82

Slide 82 text

Limitation: Serialization Common 
 Serialization Library Android
 App iOS
 App ❌

Slide 83

Slide 83 text

Limitation: Serialization Library

Slide 84

Slide 84 text

Alternative Approaches • Use example-gen branch from Kotlin Serialization library • Create a Codable protocol extension for you model on iOS • Manually decode JSON on iOS

Slide 85

Slide 85 text

Multiplatform App in MVP View Presenter Interactor Analytics

Slide 86

Slide 86 text

Multiplatform App in MVP Presenter Interactor Analytics Shared

Slide 87

Slide 87 text

Multiplatform App in MVP View
 (Android) Presenter Interactor Analytics Shared View
 (iOS)

Slide 88

Slide 88 text

MVP Contract (Common Module) class FeedContract { interface View { fun showVideos(videos: List) fun showErrorMessage(message: String) fun showVideoDetails(video : Video) } … }

Slide 89

Slide 89 text

MVP Contract (Common Module) class FeedContract { … interface Presenter : BasePresenter { fun onSelectedVideo(video: Video) } }

Slide 90

Slide 90 text

Presenter (Common Module) class FeedPresenter( private val videosInteractor: VideosInteractor ): FeedContract.Presenter {
 override fun onViewAttached(view: FeedContract.View) { this.view = view videosInteractor.fetchVideos(object: FetchDataCallback> { override fun onFetchedData(data: List) { view.showVideos(data) } override fun onError() { view.showErrorMessage("Failed to load feed.") } }) }

Slide 91

Slide 91 text

iOS Generated Classes in Objective-C • Kotlin Base • FeedContract is a protocol • VideosInteractor is a protocol • Feed Presenter

Slide 92

Slide 92 text

ViewController iOS App class FeedViewController: UITableViewController, SharedFeedContractView { override func viewDidLoad() { super.viewDidLoad() let videosInteractor = VideosInteractorImpl() feedPresenter = SharedFeedPresenter(videosInteractor: videosInteractor) feedPresenter!?.onViewAttached(view: self) } func showVideos(videos: [SharedVideo]) { self.videos = videos tableView.reloadData() }


Slide 93

Slide 93 text

Multiplatform App in MVP View
 (Android) Presenter Interactor Analytics Shared View
 (iOS)

Slide 94

Slide 94 text

• Why Kotlin Multiplatform? • Project Structure • Setting up project for Android & iOS • Sharing Presenters and Models in MVP • • Objective-C/Swift interop with Kotlin Kotlin Multiplatform for Android & iOS

Slide 95

Slide 95 text

Playing Videos Android • ExoPlayer iOS • AVKit

Slide 96

Slide 96 text

Playing Videos on iOS with Kotlin

Slide 97

Slide 97 text

cinterop cinterop C/Objective-c 
 Library Kotlin Bindings

Slide 98

Slide 98 text

Playing Videos on iOS with Kotlin

Slide 99

Slide 99 text

Kotlin Bindings for AVKit @kotlinx.cinterop.ExternalObjCClass 
 public open class AVPlayer : platform.darwin.NSObject { 
 public companion object : platform.AVFoundation.AVPlayerMeta, 
 kotlinx.cinterop.ObjCClassOf { } @kotlinx.cinterop.ObjCConstructor public constructor(
 uRL: platform.Foundation.NSURL) { !/* compiled code !*/ } 
 …

Slide 100

Slide 100 text

Playing video on iOS with Kotlin fun playVideo(view: UIView) {
 val videoURL = NSURL(string = 
 “https:!//clips.mohit.com/big_buck_bunny.mp4”) val player = AVPlayer(videoURL) val playerLayer = AVPlayerLayer() playerLayer.player = player playerLayer.frame = view.bounds view.layer.addSublayer(playerLayer) player.play() }

Slide 101

Slide 101 text

Playing video on iOS with Kotlin class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Shared().playVideo(view: self.view) } … }

Slide 102

Slide 102 text

Future • One IDE • Official common HTTP and Serialization Library • Support for pure Swift modules

Slide 103

Slide 103 text

Thank you! twitter.com/heyitsmohit /msya github.com