Slide 1

Slide 1 text

SDK Design and Publishing For Kotlin Multiplatform Mobile Kevin Galligan

Slide 2

Slide 2 text

Partner at Kotlin GDE

Slide 3

Slide 3 text

Touchlab

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Lots of Promises final of talk arc

Slide 6

Slide 6 text

context

Slide 7

Slide 7 text

Android Conference just native mobile

Slide 8

Slide 8 text

Kotlin (common) Android Framework Android Stuff iOS Stuff

Slide 9

Slide 9 text

Android side? just kotlin

Slide 10

Slide 10 text

iOS side? well…

Slide 11

Slide 11 text

Kotlin —> Xcode Framework nice, done!

Slide 12

Slide 12 text

API Not Ideal you can definitely make it worse

Slide 13

Slide 13 text

Objc

Slide 14

Slide 14 text

The Big Issues or this talk’s TOC

Slide 15

Slide 15 text

Kotlin through an Objc lens lowest common denominator

Slide 16

Slide 16 text

Modularization one framework, name collisions, …

Slide 17

Slide 17 text

Limit Public API nothing extra

Slide 18

Slide 18 text

Packaging and Team Stuff some thoughts about practical approaches

Slide 19

Slide 19 text

“Everything Else” just shove it in there

Slide 20

Slide 20 text

why bother?

Slide 21

Slide 21 text

Writing code 2x is insane! if you didn’t need to, of course

Slide 22

Slide 22 text

“Kotlin doesn’t do ___” misses the point

Slide 23

Slide 23 text

~$70k ~$65k

Slide 24

Slide 24 text

~$70k ~$0

Slide 25

Slide 25 text

Yes, Currently 2x is Best in your org, with your team, hired/trained for 2x

Slide 26

Slide 26 text

Native isn’t going away but there is consolidation

Slide 27

Slide 27 text

UX Complexity/Sensitivity 2014 Not-Native Native

Slide 28

Slide 28 text

No Precise Data obviously

Slide 29

Slide 29 text

UX Complexity/Sensitivity 2018 Not-Native Native

Slide 30

Slide 30 text

UX Complexity/Sensitivity 2022 Not-Native Native

Slide 31

Slide 31 text

UX Complexity/Sensitivity The boring middle Not-Native Native

Slide 32

Slide 32 text

UX Complexity/Sensitivity 2026? Not-Native Native

Slide 33

Slide 33 text

KMP UI Story it’s coming

Slide 34

Slide 34 text

compose UI on iOS

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

UX Complexity/Sensitivity The boring middle + Not-Native Native

Slide 37

Slide 37 text

iOS Compose will be OK probably not perfect

Slide 38

Slide 38 text

Optionally Native much lower platform risk

Slide 39

Slide 39 text

But I digress…

Slide 40

Slide 40 text

Shared Code Overhead this is the issue

Slide 41

Slide 41 text

Zero Overhead? we wouldn’t write 2x

Slide 42

Slide 42 text

All Options != kotlin focused on interop

Slide 43

Slide 43 text

If *I* was on your team kmp party!

Slide 44

Slide 44 text

Touchlab

Slide 45

Slide 45 text

That’s what we’re here to talk about… bringing us back from the technical odyssey

Slide 46

Slide 46 text

notes on the android side

Slide 47

Slide 47 text

KMP Ready just JVM

Slide 48

Slide 48 text

https://www.droidcon.com/2022/06/28/embracing- commonmain-for-android-development/

Slide 49

Slide 49 text

https://github.com/handstandsam/kmp4free

Slide 50

Slide 50 text

Just Kotlin basically

Slide 51

Slide 51 text

kotlin through an objective-c lens

Slide 52

Slide 52 text

Kotlin —> Objc lose expressiveness

Slide 53

Slide 53 text

Some Major Highlights there are others, but…

Slide 54

Slide 54 text

Default Parameters

Slide 55

Slide 55 text

inline fun i(tag: String = this.tag, throwable: Throwable? = null, message: () -> String) { //etc }

Slide 56

Slide 56 text

inline fun i(tag: String = this.tag, throwable: Throwable? = null, message: () -> String) { //etc } log.i { "Just a string" }

Slide 57

Slide 57 text

inline fun i(tag: String = this.tag, throwable: Throwable? = null, message: () -> String) { //etc } log.i { "Just a string" } log.i(tag: log.tag, throwable: nil) {"A log"}

Slide 58

Slide 58 text

Generics it’s complicated

Slide 59

Slide 59 text

No interfaces variance gets interesting

Slide 60

Slide 60 text

Coroutines also complicated

Slide 61

Slide 61 text

Compiler outputs suspend functions we don’t use them (I’ll explain)

Slide 62

Slide 62 text

(Forget Flows)

Slide 63

Slide 63 text

Enums not swift-friendly

Slide 64

Slide 64 text

Other Kotlin Structures sealed classes, etc

Slide 65

Slide 65 text

Missing Swifty Things structs, enums with values

Slide 66

Slide 66 text

fixing the api

Slide 67

Slide 67 text

Two APIs direct kotlin and “not kotlin”

Slide 68

Slide 68 text

Common Android iOS Kotlin-friendly API Direct calls Adapters Adapters for non-Kotlin Clients

Slide 69

Slide 69 text

Context dependent more or less

Slide 70

Slide 70 text

Favor Simple and Specific • Simple callbacks (unless you’re doing code gen) • Be careful with generics • Sealed classes won’t make sense • Enums are OK (but not great)

Slide 71

Slide 71 text

Reactive Bridge (AKA make coroutines work)

Slide 72

Slide 72 text

Suspend Functions we don’t use them

Slide 73

Slide 73 text

Can’t control lifecycle no coroutine context

Slide 74

Slide 74 text

Also can’t suppress them extra stuff in the interface

Slide 75

Slide 75 text

And, Flows don’t work

Slide 76

Slide 76 text

Several implementations us too (kind of)

Slide 77

Slide 77 text

KMP-NativeCoroutines

Slide 78

Slide 78 text

Missing Parallel Features in both kotlin and swift

Slide 79

Slide 79 text

Default Parameters 3 options

Slide 80

Slide 80 text

don’t worry about it (explicit params) remove them and have multiple methods swift extensions

Slide 81

Slide 81 text

Enums (without data) simple adapter

Slide 82

Slide 82 text

Different Language Features don’t blame objective-c

Slide 83

Slide 83 text

Kotlin and Swift are different syntactic similarities aside

Slide 84

Slide 84 text

Similar Problems different solutions

Slide 85

Slide 85 text

Sealed Classes -> Enums enums with associated values

Slide 86

Slide 86 text

Structs? transform from data classes?

Slide 87

Slide 87 text

Generate Swift? for the non-kotlin things

Slide 88

Slide 88 text

Direct Swift Interop won’t solve these issues

Slide 89

Slide 89 text

Generics (again) yes to interfaces, no to variance

Slide 90

Slide 90 text

A tale of 2 loggers mini self-own

Slide 91

Slide 91 text

inline fun i(tag: String = this.tag, throwable: Throwable? = null, message: () -> String) { //etc } log.i { "Just a string" } log.i(tag: log.tag, throwable: nil) {"A log"}

Slide 92

Slide 92 text

inline fun i(message: () -> String) { //etc } inline fun i(throwable: Throwable, message: () -> String) { //etc }

Slide 93

Slide 93 text

extension [Not calling them out by name…] { static func v(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .verbose, tag: tag, items, separator: separator, file: file, function: function) } static func d(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .debug, tag: tag, items, separator: separator, file: file, function: function) } static func i(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .info, tag: tag, items, separator: separator, file: file, function: function) } static func w(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .warning, tag: tag, items, separator: separator, file: file, function: function) } static func e(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .error, tag: tag, items, separator: separator, file: file, function: function) } static func a(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .assert, tag: tag, items, separator: separator, file: file, function: function) }

Slide 94

Slide 94 text

inline fun i(throwable: Throwable? = null, tag: String = this.tag, message: () -> String){ logBlock(Severity.Info, tag, throwable, message) }

Slide 95

Slide 95 text

inline fun i(throwable: Throwable? = null, tag: String = this.tag, message: () -> String){ logBlock(Severity.Info, tag, throwable, message) }

Slide 96

Slide 96 text

extension [Not calling them out by name…] { static func v(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .verbose, tag: tag, items, separator: separator, file: file, function: function) } static func d(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .debug, tag: tag, items, separator: separator, file: file, function: function) } static func i(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .info, tag: tag, items, separator: separator, file: file, function: function) } static func w(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .warning, tag: tag, items, separator: separator, file: file, function: function) } static func e(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .error, tag: tag, items, separator: separator, file: file, function: function) } static func a(tag: String? = nil, _ items: Any..., separator: String = " ", file: String = #file, function: String = #function) { log(logLevel: .assert, tag: tag, items, separator: separator, file: file, function: function) }

Slide 97

Slide 97 text

This talk is already old as I’m writing it

Slide 98

Slide 98 text

This talk is already old as I’m writing it

Slide 99

Slide 99 text

Common Android iOS Kotlin-friendly API Direct calls Adapters Adapters for non-Kotlin Clients

Slide 100

Slide 100 text

Common Android iOS Kotlin-friendly API Direct calls (Swift) Auto-gen Swift tweaks

Slide 101

Slide 101 text

“swift thing”

Slide 102

Slide 102 text

Swift Generation Library with unique parts

Slide 103

Slide 103 text

Swift Templating kotlin names aren’t known

Slide 104

Slide 104 text

Swift Linking one framework

Slide 105

Slide 105 text

Objc Header Transforms shadow stuff

Slide 106

Slide 106 text

Robust Swift Generation

Slide 107

Slide 107 text

When? droidcon nyc-ish (so, september)

Slide 108

Slide 108 text

this talk?

Slide 109

Slide 109 text

We *may* need support pitching friday

Slide 110

Slide 110 text

Swift Only! good luck with your JS, etc

Slide 111

Slide 111 text

Practical issues with “2 APIs” good and bad stuff

Slide 112

Slide 112 text

No content

Slide 113

Slide 113 text

Common Android iOS Adapters

Slide 114

Slide 114 text

Common Android iOS Adapters

Slide 115

Slide 115 text

Common Android iOS Adapters Export

Slide 116

Slide 116 text

Increasingly Rare • Swift gen makes this less useful • Some PRs from Rick Clephas fix some of these issues directly • https://github.com/JetBrains/kotlin/pull/4818 - Annotations to suppress exporting Objc/Swift methods • https://github.com/JetBrains/kotlin/pull/4815 - Better Swift naming control

Slide 117

Slide 117 text

Name Prefix blunt modularization

Slide 118

Slide 118 text

Common Android iOS Adapters MyClass CommonMyClass

Slide 119

Slide 119 text

Common Android iOS Adapters MyClass CommonMyClass Export

Slide 120

Slide 120 text

It comes up later yes there’s more

Slide 121

Slide 121 text

why you should give talks

Slide 122

Slide 122 text

(or “big tangent time”)

Slide 123

Slide 123 text

“Why am I doing this?” thought precedes every talk

Slide 124

Slide 124 text

Explaining forces Examining

Slide 125

Slide 125 text

“2 APIs” is kind of a hack and we’re trying to fix it

Slide 126

Slide 126 text

This feels a little inconsistent to me all for the better

Slide 127

Slide 127 text

Anyway… (just ping me on slack with specific questions)

Slide 128

Slide 128 text

modularization

Slide 129

Slide 129 text

Single Framework not a bug

Slide 130

Slide 130 text

TaxCalc TaxCalcKt Framework Products ProductsKt Framework AcmeRepo stdlib AcmeRepo stdlib

Slide 131

Slide 131 text

TaxCalc TaxCalcKt Framework Products ProductsKt Framework AcmeRepo stdlib AcmeRepo stdlib

Slide 132

Slide 132 text

Modularize in Kotlin Layer ci can build multiple collection frameworks

Slide 133

Slide 133 text

Name Collisions scale makes this worse

Slide 134

Slide 134 text

Swift name has no namespace co.touchlab.kermit.Logger —> Logger co.touchlab.kermit.helper.Logger —> Logger

Slide 135

Slide 135 text

Swift name has no namespace co.touchlab.kermit.Logger —> Logger co.touchlab.kermit.helper.Logger —> _Logger

Slide 136

Slide 136 text

Dependency Prefix blunt but sort of reduces the problem

Slide 137

Slide 137 text

Multiple Modules still one framework

Slide 138

Slide 138 text

multiple modules/namespaces

Slide 139

Slide 139 text

limit exposed api

Slide 140

Slide 140 text

Shared Models, Data, Networking Shared Framework Framework from Single Module

Slide 141

Slide 141 text

Shared Models, Data, Networking Shared Framework Everything visible, or referenced

Slide 142

Slide 142 text

Shared Models, Data, Networking Shared Framework Dependencies Kermit kotlinx.coroutines SqlDelight getLogger():Logger getLogger():Logger

Slide 143

Slide 143 text

Shared Models, Data, Networking Shared Framework Dependencies Kermit kotlinx.coroutines SqlDelight getLogger():Logger getLogger():KermitLogger

Slide 144

Slide 144 text

Shared Models, Data, Networking Shared Framework Dependencies Kermit kotlinx.coroutines SqlDelight getLogger():Logger getLogger():Logger

Slide 145

Slide 145 text

Shared Models, Data, Networking Shared Framework Who cares?

Slide 146

Slide 146 text

Bigger header hard to parse (for a developer)

Slide 147

Slide 147 text

Bigger binary each artifact gets an Objc adapter/logic

Slide 148

Slide 148 text

Non-visible code is still there not in header and no extra binary

Slide 149

Slide 149 text

Mark “internal” whatever you can

Slide 150

Slide 150 text

Look for dependency refs scan the header

Slide 151

Slide 151 text

Consider wrapping things for extreme cases

Slide 152

Slide 152 text

Shared Models, Networking Shared Framework (Maybe) Hide with modules Kermit kotlinx.coroutines SqlDelight someData():DataMyData Data someData():MyData

Slide 153

Slide 153 text

Export with caution especially transitive

Slide 154

Slide 154 text

Measuring binary size not on-disk size

Slide 155

Slide 155 text

Measuring KMP’s impact not just “app size”

Slide 156

Slide 156 text

Push to app store gold standard

Slide 157

Slide 157 text

Measure locally https://docs.flutter.dev/perf/app-size#ios

Slide 158

Slide 158 text

We have a plugin just tells you

Slide 159

Slide 159 text

packaging and team

Slide 160

Slide 160 text

Quick Thoughts whole different talk

Slide 161

Slide 161 text

Generalizations every team is different

Slide 162

Slide 162 text

Just another SDK “and it better work!”

Slide 163

Slide 163 text

Common iOS Framework Dev Dev App Publish (XC)Framework Dev Big App Other Team Consumes (XC)Framework

Slide 164

Slide 164 text

Unwanted Guest always gets blamed first

Slide 165

Slide 165 text

Everybody Writes Kotlin all new tools, build disruption

Slide 166

Slide 166 text

Common iOS Framework Dev Dev App Publish (XC)Framework Dev Big App Other Team Consumes (XC)Framework iOS Devs Android Devs

Slide 167

Slide 167 text

Common iOS Framework Dev App Publish (XC)Framework

Slide 168

Slide 168 text

Common iOS Framework Dev App Publish (XC)Framework

Slide 169

Slide 169 text

Common iOS Framework Dev App Publish (XC)Framework iOS Devs Android Devs

Slide 170

Slide 170 text

Common iOS Framework Dev App Publish (XC)Framework Mobile Devs Mobile Devs

Slide 171

Slide 171 text

Reach out if interested see “feedback we want” slide…

Slide 172

Slide 172 text

“everything else”

Slide 173

Slide 173 text

PR’s and YT’s to Watch https://github.com/touchlab/KaMPKit/discussions/252

Slide 174

Slide 174 text

Feedback We Want Swift generation Introducing KMP to your team How your iOS code/team is structured

Slide 175

Slide 175 text

Feedback We Want https://github.com/touchlab/KaMPKit/discussions/253

Slide 176

Slide 176 text

We’re hiring! https://touchlab.co/careers-3/

Slide 177

Slide 177 text

No content

Slide 178

Slide 178 text

Thanks! @kpgalligan