Slide 1

Slide 1 text

Copenhagen Denmark SHIPPING MULTIPLATFORM ON IOS & ANDROID BEN ASHER ALEC STRONG @benasher44 @strongolopolis

Slide 2

Slide 2 text

Link to another sheet Markups

Slide 3

Slide 3 text

History

Slide 4

Slide 4 text

Live Info

Slide 5

Slide 5 text

Championed on iOS ~15 across Android and iOS Construction Championed on Android ~30 across Android and iOS Financial Services

Slide 6

Slide 6 text

Offline Sync System Search Business Logic

Slide 7

Slide 7 text

MPP Business Logic Models

Slide 8

Slide 8 text

MPP Business Logic Models

Slide 9

Slide 9 text

MPP

Slide 10

Slide 10 text

MPP Kotlin/Native

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

New tools New language Uneven ecosystem !!! !!!

Slide 14

Slide 14 text

Adopting cross-platform in your app has a cost

Slide 15

Slide 15 text

What is the value that makes it worth the effort?

Slide 16

Slide 16 text

Diverging Sync Systems

Slide 17

Slide 17 text

Existing Shared Code

Slide 18

Slide 18 text

What tools will your cross-platform solution need What is the common goal all stakeholders have

Slide 19

Slide 19 text

What else? No JNI on Android Obj-C
 Interop Not New For Android Community Jetbrains

Slide 20

Slide 20 text

Common Questions Kotlin/Native relies on Obj-C? What about Swift? Obj-C header is well-annotated for Swift This is the status quo for Apple’s own frameworks

Slide 21

Slide 21 text

Common Questions What about performance? Same performance expectations on Android Can drop into C if needed on iOS

Slide 22

Slide 22 text

Common Questions What about value types?

Slide 23

Slide 23 text

Common Questions There’s a garbage collector? But there’s no JVM, right? Bacon’s Algorithm for GC

Slide 24

Slide 24 text

Common Questions What libraries can we use for mpp? JVM Distributions are incomplete Special multiplatform distributions K/N ABI instability (klib)

Slide 25

Slide 25 text

Common Questions What is “common” code?

Slide 26

Slide 26 text

commonMain iosMain androidMain iOS - .framework Android - .aar Library Outputs Library Source Sets

Slide 27

Slide 27 text

commonMain iosMain androidMain iOS - .framework Android - .aar Library Outputs Library Source Sets Code expect class Lock actual class Lock actual class Lock

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

expect class Lock actual class Lock actual class Lock commonMain iosMain androidMain iOS - .framework Android - .aar Library Outputs Library Source Sets

Slide 31

Slide 31 text

commonMain iosMain androidMain iOS - .framework Android - .aar Library Outputs Library Source Sets

Slide 32

Slide 32 text

commonMain iosMain androidMain iOS - .framework Android - .aar Library Outputs Library Source Sets

Slide 33

Slide 33 text

commonMain iosMain androidMain iOS - .framework Android - .aar Library Outputs Library Source Sets windowsMain Windows - .dll

Slide 34

Slide 34 text

Is it platform agnostic? commonMain Yes No Will other common code use it? Yes No Platform

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

Is it platform agnostic? No Will other common code use it? commonMain Yes

Slide 37

Slide 37 text

expect class DatabaseFactory { internal fun createSqliteDriver(): SqlDriver }

Slide 38

Slide 38 text

Is it platform agnostic? No Will other common code use it? No Platform

Slide 39

Slide 39 text

sourceSets {A commonMain {B}C androidLibMain {D}E iosLibMain {F}G }H

Slide 40

Slide 40 text

sourceSets {A commonMain {B}C androidLibMain {D dependencies {I api project(‘:protos') }J }E iosLibMain {F}G }H

Slide 41

Slide 41 text

fun SearchQueries.insert(entity: SyncEntity) entity: SyncEntity

Slide 42

Slide 42 text

sourceSets {A commonMain {B}C androidLibMain {D}E iosLibMain {F}G }H

Slide 43

Slide 43 text

sourceSets {A commonMain {B}C androidLibMain {D}E }H

Slide 44

Slide 44 text

Is it platform agnostic? commonMain Yes

Slide 45

Slide 45 text

fun search(terms: String): List?

Slide 46

Slide 46 text

commonMain iosMain androidMain Library Source Sets Host
 Application

Slide 47

Slide 47 text

Host
 Application Talks to backend API Talks to existing application state

Slide 48

Slide 48 text

Host
 Application commonMain Library Initialization Library defines interface App provides implementation

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

Highly platform-specific Backend API Hosts Current user’s credential Case Study: Networking

Slide 51

Slide 51 text

Make an API request Receive an async response Interface can be re-implemented later MPP Life Hack: Interface Network

Slide 52

Slide 52 text

interface Network { /** * Makes an API request */ fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable }

Slide 53

Slide 53 text

interface Network { /** * Makes an API request */ fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable } host: APIHost Plain enum of names of the hosts– no URL handling required

Slide 54

Slide 54 text

interface Network { /** * Makes an API request */ fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable } request: Request data class: - String path and body, - query params dict - enum method (GET, POST, etc.)

Slide 55

Slide 55 text

interface Network { /** * Makes an API request */ fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable } completion: (Request.Response) -> Unit sealed class w/ different response types:

Slide 56

Slide 56 text

interface Network { /** * Makes an API request */ fun makeRequest( host: APIHost, request: Request, completion: (Request.Response) -> Unit ): NetworkDisposable } NetworkDisposable Interface with a cancel() method

Slide 57

Slide 57 text

suspend fun Network.makeRequest( host: APIHost, request: Request ): Request.Response

Slide 58

Slide 58 text

Concurrency in commonMain commonMain iosMain androidMain expect fun T.freeze(): T actual fun T.freeze(): T = this actual fun T.freeze(): T = freeze()

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

Shipping Internally commonMain iosMain androidMain Host
 Application

Slide 62

Slide 62 text

commonMain iosMain androidMain Host
 Application Host
 Application

Slide 63

Slide 63 text

commonMain iosMain androidMain Host
 Application Host
 Application

Slide 64

Slide 64 text

Pros Cons Minimal change for android Seamless local development Single source of truth Host dependencies Two build environments for iOS CI Nightmare

Slide 65

Slide 65 text

commonMain iosMain androidMain Host
 Application Host
 Application commonMain

Slide 66

Slide 66 text

Pros Cons Minimal change for android Seamless local development Host dependencies Two build environments for iOS CI Nightmare for iOS Out of sync code

Slide 67

Slide 67 text

commonMain iosMain androidMain Host
 Application Host
 Application

Slide 68

Slide 68 text

Pros Cons CI is fast Encourages unit test coverage Single source of truth Breaking changes are painful Need good CD setup Debugging decoupled repos

Slide 69

Slide 69 text

commonMain iosMain Host
 Application Host
 Application androidMain

Slide 70

Slide 70 text

commonMain androidMain Host
 Application Host
 Application

Slide 71

Slide 71 text

Shipping Externally Test Crashing!

Slide 72

Slide 72 text

Shipping Externally fun crash() { (null as String?)!! }

Slide 73

Slide 73 text

Shipping Externally setUnhandledExceptionHook()

Slide 74

Slide 74 text

Shipping Externally What happens if a Worker crashes?

Slide 75

Slide 75 text

No content

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

No content

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

Define your project’s goals Be thoughtful growing your codebase Avoid solving general muliplatform problems Get your CI and dev setup working early Join us in Kotlin slack Review