SlideShare a Scribd company logo
1 of 52
Writing Kotlin Multiplatform
libraries that your iOS
teammates are gonna love
André Oriani
Disclaimer
My presentation, comments and
opinions are provided in my personal
capacity and not as a representative
of Walmart. They do not reflect the
views of Walmart and are not
endorsed by Walmart.
TWITTER.COM/AORIANI
ANDROIDDEV.SOCIAL/@AORIANI
MEDIUM.COM/@AORIANI
GITHUB.COM/AORIANI
LINKEDIN.COM/IN/AORIANI
• Italian🇮🇹-Brazilian🇧🇷
• Working professionally with Android since its
first public releases.
• Worked on the development of the Homescreen and
the Instant Messaging app for the very first
Motorola Android phones, including the DROID
series.
• Currently tech lead at Walmart in the Silicon
Valley
André Oriani
https://bit.ly/kmp-swift
Kotlin Multiplatform is awesome!
In order to accomplish our secret goal of complete
domination of the world of mobile development…
Kotlin and Swift are so similar…
EXPORTED AS BRIDGE TO
ONAN
Presentation Format
//KOTLIN API
data class Person(
val name: String,
val age: Int
)
//EXPORTED OBJ-C HEADER
__attribute__((objc_subclassing_restricted))
__attribute__((swift_name("Person")))
@interface SharedPerson : SharedBase
- (instancetype)initWithName:(NSString *)name age:(int32_t)age
__attribute__((swift_name("init(name:age:)")))
__attribute__((objc_designated_initializer));
- (SharedPerson *)doCopyName:(NSString *)name age:(int32_t)age
__attribute__((swift_name("doCopy(name:age:)")));
- (BOOL)isEqual:(id _Nullable)other
__attribute__((swift_name("isEqual(_:)")));
- (NSUInteger)hash __attribute__((swift_name("hash()")));
- (NSString *)description
__attribute__((swift_name("description()")));
@property (readonly) int32_t age
__attribute__((swift_name("age")));
@property (readonly) NSString *name
__attribute__((swift_name("name")));
@end
// SWIFT CLIENT CODE
let keanuReeves = Person(name: "Keanu Charles Reeves", age: 59)
[OBJ-C]
Presentation Format
//KOTLIN API
data class Person(
val name: String,
val age: Int
)
//HEADER “TRANSLATED” TO SWIFT
public class Person : KotlinBase {
public init(name: String, age: Int32)
open func doCopy(name: String, age:
Int32) -> Person
open func isEqual(_ other: Any?) ->
Bool
open func hash() -> UInt
open func description() -> String
open var age: Int32 { get }
open var name: String { get }
}
// Which one is which?
let keanuReeves = Person(name: "Keanu Charles Reeves", age: 59)
Name
Clashing
Name Clashing
THE
PROBLEM
• Kotlin
• Multiple levels of namespace due to packages;
• Method signature is the method name plus parameter types.
• Objective-C
• No support to namespaces;
• Method signature is the method name plus parameter names.
• Swift
• One level of namespace: the exported framework;
• Method signature is the method name plus parameter names.
Classes with same name but different packages
package io.aoriani.network
class Item
...
package io.aoriani.models
class Item
public class Item : KotlinBase {
public init()
}
public class Item_ : KotlinBase {
public init()
}
// Which one is which?
let item1 = Item()
let item2 = Item_()
THE
PROBLEM
Classes with same name but different packages
package io.aoriani.network
@ObjCName("ItemResponse")
class Item
...
package io.aoriani.models
@ObjCName("ItemModel")
class Item
public class ItemResponse :
KotlinBase {
public init()
}
public class ItemModel : KotlinBase {
public init()
}
let itemDomainModel = ItemModel()
let itemNetworkResponse = ItemResponse()
THE
SOLUTION
Overloading with params named the same
fun sort(data: List<Int>){}
fun sort(data: Map<String, Int>){}
public class ExampleKt : KotlinBase {
open class func sort(data: [KotlinInt])
open class func sort(data_ data:
[String : KotlinInt])
}
ExampleKt.sort(data: [1, 2, 3])
ExampleKt.sort(data_: ["A" : 1, "B": 2])
THE
PROBLEM
Overloading with params named the same
fun sort(listOfInts: List<Int>){}
fun sort(mapOfStringToInt: Map<String,
Int>){}
public class ExampleKt : KotlinBase {
open class func sort(listOfInts:
[KotlinInt])
open class func sort(mapOfStringToInt:
[String : KotlinInt])
}
ExampleKt.sort(listOfInts: [1, 2, 3])
ExampleKt.sort(mapOfStringToInt: ["A" : 1, "B": 2])
THE
SOLUTION
Interface fields, same name, different types
interface Person {
val id: String
}
interface Robot {
val id: Long
}
public protocol Person {
var id: String { get }
}
public protocol Robot {
var id_: Int64 { get }
}
// Only possible because Konan renamed field for Robot
class Android: Person, Robot {
let id: String = ""
let id_: Int64 = 0
}
THE
PROBLEM
Disappearing
Types
Collection with types conforming to protocol
interface Product
class Consumer {
fun consume(products: Set<Product>) {}
}
public protocol Product {}
public class Consumer : KotlinBase {
public init()
open func consume(products:
Set<AnyHashable>)
}
// Compiles fine in Swift
let consumer = Consumer()
let fakeProducts: Set<Int> = [1, 2, 3]
consumer.consume(products: fakeProducts)
THE
PROBLEM
Collection with types conforming to protocol
THE
PROBLEM
Set<Product> NSSet<id<Product>>
Set<T>
where T: Hashable
[OBJ-C]
MAPS TO BRIDGES TO
• Swift Set requires elements conforming to protocol Hashable;
• Kotlin Interfaces are exported as Objetive-C protocols;
• Objective-C protocols cannot conform to other protocols, let alone Swift Protocols, thus the
argument type is erased to AnyHashable;
• All KMP Classes inherit from NSObject which conforms to Hashable;
Collection with types conforming to protocol
abstract class Product
class Consumer {
fun consume(products: Set<Product>) {}
}
open class Product : KotlinBase {
public init()
}
public class Consumer : KotlinBase {
public init()
open func consume(products:
Set<Product>)
}
let consumer = Consumer()
let fakeProducts: Set<Int> = [1, 2, 3]
// Error: Cannot convert value of type 'Set<Int>' to expected
argument type 'Set<Product>'
//consumer.consume(products: fakeProducts)
let realProducts: Set<Product> = [Product(), Product()]
consumer.consume(products: realProducts)
THE
SOLUTION
Values classes
@JvmInline
value class PostalCode(private val code:
String)
class Letter(val zipcode: PostalCode)
public class Letter : KotlinBase {
public init(zipcode: Any)
open var zipcode: Any { get }
}
//This compiles fine
let letter = Letter(postalCode: UIViewController())
THE
PROBLEM
Providing
convenience
Interface extensions
interface Person {
val firstName: String
val lastName: String
}
fun Person.fullName() = "$firstName
$lastName"
public protocol Person {
var firstName: String { get }
var lastName: String { get }
}
public class ExampleKt : KotlinBase {
open class func fullName(_ receiver:
Person) -> String
}
class SwiftPerson: Person {…}
let swiftPerson = SwiftPerson(firstName: "Bruce", lastName: "Wayne")
// Java déjà vu?
print(ExampleKt.fullName(swiftPerson))
THE
PROBLEM
Class extensions
abstract class Person(
val firstName: String,
val lastName: String
)
fun Person.fullName() = "$firstName
$lastName"
open class Person : KotlinBase {
public init(firstName: String,
lastName: String)
open var firstName: String { get }
open var lastName: String { get }
}
extension Person {
open func fullName() -> String
}
class SwiftPerson: Person {…}
let swiftPerson = SwiftPerson(firstName: "Bruce", lastName: "Wayne")
print(swiftPerson.fullName())
THE
SOLUTION
Filling the gaps between Kotlin and Swift
class MyRange {
fun isInRange(value: Int, range: IntRange) =
value in range
}
fun test(){
MyRange().isInRange(1, -7..6)
}
public class MyRange : KotlinBase {
public init()
open func isInRange(value: Int32,
range: KotlinIntRange) -> Bool
}
let myRange = MyRange()
// This is a bit clumsy
myRange.isInRange(value: 1, range: KotlinIntRange(start: -1,
endInclusive: 2))
THE
PROBLEM
Filling the gaps between Kotlin and Swift
class MyRange {
@ShouldRefineInSwift
fun isInRange(value: Int, range: IntRange) =
value in range
}
public class MyRange : KotlinBase {
public init()
}
let myRange = MyRange()
myRange.isRange(value: 2, range: -1...2)
extension MyRange {
func isInRange(value value: Int32,
range range: ClosedRange<Int32>) -> Bool {
return __is(inRangeValue: value,
range: KotlinIntRange(start:
range.lowerBound, endInclusive:
range.lowerBound))
}
}
THE
SOLUTION
Providing only platform specific code
import io.ktor.http.Url
import java.net.URL
fun Url.toURL(): URL =
URL(this.toString())
let url = Service.shared.baseUrl.toURL()
let session = URLSession(configuration: .default)
session.dataTask(with: url) {
...
extension Ktor_httpUrl {
open func toURL() -> URL
}
import io.ktor.http.Url
import platform.Foundation.NSURL
fun Url.toURL(): NSURL =
NSURL(string = toString())
androidMain
iosMain
THE
SOLUTION
Default arguments
fun compareString(a: String, b: String,
ignoreCase: Boolean = false): Boolean {
return a.equals(b, ignoreCase =
ignoreCase)
}
public class ExampleKt : KotlinBase {
open class func compareString(a:
String, b: String, ignoreCase: Bool) ->
Bool
}
let result1 = ExampleKt.compareString(a: "HELLO", b: "hello", ignoreCase:
true)
// Compile error: Missing argument for parameter 'ignoreCase' in call
let result2 = ExampleKt.compareString(a: "Roma", b: "Londres")
THE
PROBLEM
Exceptions
Exceptions
class Bomb {
fun explode() : Unit =
throw IOException(”Bomb has detonated")
}
public class Bomb : KotlinBase {
public init()
open func explode()
}
func defuseBomb() {
let bomb = Bomb()
do {
bomb.explode()
} catch {
// 'catch' block is unreachable because no errors are thrown in 'do'
block
print("Bomb disarmed!")
}
}
THE
PROBLEM
Function doesn't have or inherit @Throws annotation and thus exception isn't propagated
from Kotlin to Objective-C/Swift as NSError.
It is considered unexpected and unhandled instead. Program will be terminated.
Uncaught Kotlin exception: io.ktor.utils.io.errors.IOException: Bomb has detonated
at
0 shared 0x103e6ec95 kfun:kotlin.Exception#<init>(kotlin.String?;kotlin.Throwable?){} + 133
(/opt/buildAgent/work/f43969c6214a19e7/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:25:63)
at
1 shared 0x1040c6de7 kfun:io.ktor.utils.io.errors.IOException#<init>(kotlin.String;kotlin.T
hrowable?){} + 119 (/opt/buildAgent/work/8d547b974a7be21f/ktor-io/posix/src/io/ktor/utils/io/errors/IOException.kt:4:58)
at
2 shared 0x1040c6e4d kfun:io.ktor.utils.io.errors.IOException#<init>(kotlin.String){} + 93
(/opt/buildAgent/work/8d547b974a7be21f/ktor-io/posix/src/io/ktor/utils/io/errors/IOException.kt:5:50)
at 3 shared 0x103dc6591 kfun:io.aoriani.kmpapp.Bomb#explode(){} + 145
at 4 shared 0x103dc8c54 objc2kotlin_kfun:io.aoriani.kmpapp.Bomb#explode(){} + 148
at 5 iosApp 0x1027a6c59 $s6iosApp10defuseBombyyF + 57
(/Users/aoriani/Development/Multiplatform/KmpApp/iosApp/iosApp/ContentView.swift:33:14)
at 6 iosApp 0x1027a6f5d $s6iosApp11ContentViewVACycfC + 141
(/Users/aoriani/Development/Multiplatform/KmpApp/iosApp/iosApp/ContentView.swift:10:0)
at 7 iosApp 0x1027a67eb $s6iosApp6iOSAppV4bodyQrvgAA11ContentViewVyXEfU_ + 27
(/Users/aoriani/Development/Multiplatform/KmpApp/iosApp/iosApp/iOSApp.swift:7:4)
at 8 SwiftUI 0x105435a01 get_witness_table
7SwiftUI4ViewRzlAA15ModifiedContentVyxAA25ComplicationIdiomModifierVGAaBHPxAaBHD1__AfA0cH0HPyHCHCTm + 60924
at 9 iosApp 0x1027a66cc $s6iosApp6iOSAppV4bodyQrvg + 156 (/
CRASH!
Exceptions
class Bomb {
@Throws(IOException::class)
fun explode() : Unit =
throw IOException("Error")
}
public class Bomb : KotlinBase {
public init()
open func explode() throws
}
func defuseBomb() {
let bomb = Bomb()
do {
try bomb.explode()
} catch let error as NSError {
switch error.kotlinException
{
case let ioe as
Ktor_ioIOException:
print("Bomb defused:
(ioe.message ?? "")")
default:
print("Sorry could not
defuse!")
}
}
}
- (BOOL)explodeAndReturnError:(NSError
* _Nullable * _Nullable)error
__attribute__((swift_name(”explode()"))
);
[OBJ-C]
Konan
Xcode Bridging
THE
SOLUTION
Enums
& Sealed Classes
Kotlin Enums vs Swift Enums
enum class Result {
LOADING,
SUCCESS,
FAILURE
}
public class Result : KotlinEnum<Result> {
open class var loading: Result { get }
open class var success: Result { get }
open class var failure: Result { get }
open class func values() ->
KotlinArray<Result>
open class var entries: [Result] { get
}
}
func testEnum() {
let result: Result = .failure
switch result {
case .loading: print("Loading")
case .success: print("Success")
case .failure: print("Failed")
default: print("I will never be called")
}
}
THE
PROBLEM
Kotlin Sealed Class vs Swift Enums
sealed class Result {
data object Loading: Result()
class Success(val value: String): Result()
class Failure(val throwable: Throwable): Result()
}
open class Result : KotlinBase {}
extension Result {
public class Loading : Result {
public convenience init()
open class var shared: Result.Loading {
get }
open func isEqual(_ other: Any?) -> Bool
open func hash() -> UInt
open func description() -> String
}
public class Failure : Result {
public init(throwable: KotlinThrowable)
open var throwable: KotlinThrowable { get
}
}
public class Success : Result {
public init(value: String)
open var value: String { get }
}
}
func testSealedClass() {
let result: Result =
Result.Loading.shared
switch result {
case is Result.Loading:
print("loading")
case let success as Result.Success:
print("Success: (success.value)")
case let failure as Result.Failure:
print("Failure:
(failure.throwable.message ?? "")")
default: print("I will never be called")
}
}
THE
SOLUTION
Kotlin Sealed Class vs Swift Enums
func testWrappedSealedClass() {
if let result =
SwiftResult(Result.Loading.shared) {
switch result {
case .loading:
print("Loading")
case let .success(value):
print("Success: (value)")
case let .failure(throwable):
print("Failure:
(throwable.message ?? "")")
}
}
}
enum SwiftResult {
case loading
case success(value: String)
case failure(throwable: KotlinThrowable)
}
extension SwiftResult {
init?(_ result: Result) {
switch result {
case is Result.Loading:
self = .loading
case let success as Result.Success:
self = .success(value:
success.value)
case let failure as Result.Failure:
self = .failure(throwable:
failure.throwable)
default: return nil
}
}
}
Libraries that can create the wrapper
for you:
• MOKO KSwift
• Touchlab SKIE
THE
SOLUTION
Generics
Generics
@property (nonatomic,strong) NSArray *arrayOfUrls;
@property (nonatomic,strong) NSDictionary *myDictNameToAge;
@property (nonatomic,strong) NSSet *setOfStrings;
open var arrayOfUrls: [Any]
open var myDictNameToAge: [AnyHashable : Any]
open var setOfStrings: Set<AnyHashable>
@property (nonatomic,strong) NSArray<NSURL *> *arrayOfUrls;
@property (nonatomic,strong) NSDictionary<NSString *, NSNumber *> *myDictNameToAge;
@property (nonatomic,strong) NSSet<NSString *> *setOfStrings;
open var arrayOfUrls: [URL]
open var myDictNameToAge: [String : NSNumber]
open var setOfStrings: Set<String>
[OBJ-C]
[OBJ-C]
Before Lightweight Generics
After Lightweight Generics
Generics : Protocols
interface Cat<T> {
val data: T
}
public protocol Cat {
var data: Any? { get }
}
Generics : Classes
class Dog<T>(val data: T)
// Bounded Type Parameter
class Bird<T: Number>(val data: T)
public class Dog<T> : KotlinBase
where T : AnyObject {
public init(data: T?)
open var data: T? { get }
}
//Bounded Typed Parameter
public class Bird<T> : KotlinBase
where T : AnyObject {
public init(data: T)
open var data: T { get }
}
Generics : Methods & Functions
interface MyInterface
class MethodTest {
fun <T: Number> eat(value: T): Int =
value.toInt()
fun <T: MyInterface> drink(value: T) = a
}
public protocol MyInterface {}
public class MethodTest : KotlinBase {
public init()
open func eat(value: Any) -> Int32
open func drink(value: MyInterface) ->
MyInterface
}
Flows
& Coroutines
Coroutines
class SuspendFunctions {
suspend fun generateInteger(): Int {
delay(1_000)
return 1
}
}
public class SuspendFunctions :
KotlinBase {
public init()
open func
generateInteger(completionHandler:
@escaping (KotlinInt?, Error?) -> Void)
// After Swift 5.5 we also have
open func generateInteger() async
throws -> KotlinInt
}
func usingTask() {
let task = Task { @MainActor in
do {
let object = SuspendFunctions()
let result = try await
object.generateInteger()
print("Result: (result)")
} catch {
print("We had an error:
(error)")
}
}
// This doesn't do what do you think it
would do
task.cancel()
}
THE
PROBLEM
Flows
class Flows {
fun generateIntegers(): Flow<Int> {
return flowOf(1,2,3)
}
}
public class Flows : KotlinBase {
public init()
open func genenerateIntegers() ->
Kotlinx_coroutines_coreFlow
}
THE
PROBLEM
KMP-NativeCouroutines
class CoroutinesAndFlows {
@NativeCoroutineScope
internal val scope = MainScope()
@NativeCoroutines
suspend fun randomInt(): Int {
delay(1_000);
return (1..10).random()
}
@NativeCoroutines
fun flowOfInt(value: Int): Flow<Int> {
return flowOf(value)
}
}
THE
SOLUTION
KMP-NativeCouroutines: Coroutine
import KMPNativeCoroutinesAsync
func usingNativeSuspend() {
let task = Task {
let object = CoroutinesAndFlows()
do {
let v = try await asyncFunction(for: object.randomInt())
} catch {
print("We met an error: (error)")
}
}
//Cancels Swift task and Kotlin Job
task.cancel()
}
THE
SOLUTION
KMP-NativeCouroutines: Flow
import Combine
import KMPNativeCoroutinesCombine
func usingNativeFlow() {
let object = CoroutinesAndFlows()
let publisher = createPublisher(for: object.flowOfInt(value: 3))
let cancellable = publisher.sink { completion in
switch completion {
case .finished:
print("Flow has finished")
case let .failure(err):
print("Flow terminated with error (err)")
}
} receiveValue: { value in
print("Received (value)")
}
// Cancels Kotlin Flow and Combine Publisher
cancellable.cancel()
}
THE
SOLUTION
And if you found everything too complicated…
Just use Compose Multiplatform!😁
Acknowledgement
• Thomas Hultgren
• Daniel Youn
• Paolo Rotolo
• John O’Reilly
• Pamela Hill
• Better Programming
• Towards Devs
• ProAndroidDev
• Android Weekly
• Kotlin Weekly
&

More Related Content

What's hot

Mobile Test Automation - Appium
Mobile Test Automation - AppiumMobile Test Automation - Appium
Mobile Test Automation - AppiumMaria Machlowska
 
AngularJS for Beginners
AngularJS for BeginnersAngularJS for Beginners
AngularJS for BeginnersEdureka!
 
Flutter vs xamarin vs react native - Mobile App Development Framework
Flutter vs xamarin vs react native - Mobile App Development FrameworkFlutter vs xamarin vs react native - Mobile App Development Framework
Flutter vs xamarin vs react native - Mobile App Development Frameworkdeveloperonrents
 
Gatling - Stress test tool
Gatling - Stress test toolGatling - Stress test tool
Gatling - Stress test toolKnoldus Inc.
 
Test-Driven Machine Learning
Test-Driven Machine LearningTest-Driven Machine Learning
Test-Driven Machine LearningC4Media
 
SOAP-UI The Web service Testing
SOAP-UI The Web service TestingSOAP-UI The Web service Testing
SOAP-UI The Web service TestingGanesh Mandala
 
Automatisations des tests fonctionnels avec Robot Framework
Automatisations des tests fonctionnels avec Robot FrameworkAutomatisations des tests fonctionnels avec Robot Framework
Automatisations des tests fonctionnels avec Robot Frameworklaurent bristiel
 
Best Practices for Centrally Monitoring Resource Configuration & Compliance (...
Best Practices for Centrally Monitoring Resource Configuration & Compliance (...Best Practices for Centrally Monitoring Resource Configuration & Compliance (...
Best Practices for Centrally Monitoring Resource Configuration & Compliance (...Amazon Web Services
 
React Development with the MERN Stack
React Development with the MERN StackReact Development with the MERN Stack
React Development with the MERN StackTroy Miles
 
What is an Application programming interface(API)?
What is an Application programming interface(API)?What is an Application programming interface(API)?
What is an Application programming interface(API)?Akmal Ali
 
certificat de fin d’études secondaires
certificat de fin d’études secondairescertificat de fin d’études secondaires
certificat de fin d’études secondairesLaurent ACHER
 
Rest API with Swagger and NodeJS
Rest API with Swagger and NodeJSRest API with Swagger and NodeJS
Rest API with Swagger and NodeJSLuigi Saetta
 
JMeter vs LoadRunner | Edureka
JMeter vs LoadRunner | EdurekaJMeter vs LoadRunner | Edureka
JMeter vs LoadRunner | EdurekaEdureka!
 
Chat Room System using Java Swing
Chat Room System using Java SwingChat Room System using Java Swing
Chat Room System using Java SwingTejas Garodia
 

What's hot (20)

Mobile Test Automation - Appium
Mobile Test Automation - AppiumMobile Test Automation - Appium
Mobile Test Automation - Appium
 
Codeigniter
CodeigniterCodeigniter
Codeigniter
 
AngularJS for Beginners
AngularJS for BeginnersAngularJS for Beginners
AngularJS for Beginners
 
Flutter vs xamarin vs react native - Mobile App Development Framework
Flutter vs xamarin vs react native - Mobile App Development FrameworkFlutter vs xamarin vs react native - Mobile App Development Framework
Flutter vs xamarin vs react native - Mobile App Development Framework
 
Gatling - Stress test tool
Gatling - Stress test toolGatling - Stress test tool
Gatling - Stress test tool
 
Automation Testing by Selenium Web Driver
Automation Testing by Selenium Web DriverAutomation Testing by Selenium Web Driver
Automation Testing by Selenium Web Driver
 
Test-Driven Machine Learning
Test-Driven Machine LearningTest-Driven Machine Learning
Test-Driven Machine Learning
 
FOTA Upgrade on Automotive and IoT Industry
FOTA Upgrade on Automotive and IoT IndustryFOTA Upgrade on Automotive and IoT Industry
FOTA Upgrade on Automotive and IoT Industry
 
SOAP-UI The Web service Testing
SOAP-UI The Web service TestingSOAP-UI The Web service Testing
SOAP-UI The Web service Testing
 
Automatisations des tests fonctionnels avec Robot Framework
Automatisations des tests fonctionnels avec Robot FrameworkAutomatisations des tests fonctionnels avec Robot Framework
Automatisations des tests fonctionnels avec Robot Framework
 
Best Practices for Centrally Monitoring Resource Configuration & Compliance (...
Best Practices for Centrally Monitoring Resource Configuration & Compliance (...Best Practices for Centrally Monitoring Resource Configuration & Compliance (...
Best Practices for Centrally Monitoring Resource Configuration & Compliance (...
 
Kotlin
KotlinKotlin
Kotlin
 
E call ppt
E call pptE call ppt
E call ppt
 
React Development with the MERN Stack
React Development with the MERN StackReact Development with the MERN Stack
React Development with the MERN Stack
 
What is an Application programming interface(API)?
What is an Application programming interface(API)?What is an Application programming interface(API)?
What is an Application programming interface(API)?
 
certificat de fin d’études secondaires
certificat de fin d’études secondairescertificat de fin d’études secondaires
certificat de fin d’études secondaires
 
Rest API with Swagger and NodeJS
Rest API with Swagger and NodeJSRest API with Swagger and NodeJS
Rest API with Swagger and NodeJS
 
Introducing AWS Device Farm
Introducing AWS Device FarmIntroducing AWS Device Farm
Introducing AWS Device Farm
 
JMeter vs LoadRunner | Edureka
JMeter vs LoadRunner | EdurekaJMeter vs LoadRunner | Edureka
JMeter vs LoadRunner | Edureka
 
Chat Room System using Java Swing
Chat Room System using Java SwingChat Room System using Java Swing
Chat Room System using Java Swing
 

Similar to Writing Kotlin Multiplatform libraries that your iOS teammates are gonna love

Introduction to Koltin for Android Part I
Introduction to Koltin for Android Part I Introduction to Koltin for Android Part I
Introduction to Koltin for Android Part I Atif AbbAsi
 
Being Expressive in Code
Being Expressive in CodeBeing Expressive in Code
Being Expressive in CodeEamonn Boyle
 
MOOC_PRESENTATION_KOTLIN[1].pptx
MOOC_PRESENTATION_KOTLIN[1].pptxMOOC_PRESENTATION_KOTLIN[1].pptx
MOOC_PRESENTATION_KOTLIN[1].pptxkamalkantmaurya1
 
Having Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo SurabayaHaving Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo SurabayaDILo Surabaya
 
Android & Kotlin - The code awakens #01
Android & Kotlin - The code awakens #01Android & Kotlin - The code awakens #01
Android & Kotlin - The code awakens #01Omar Miatello
 
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019UA Mobile
 
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019Eugene Kurko
 
Why Spring <3 Kotlin
Why Spring <3 KotlinWhy Spring <3 Kotlin
Why Spring <3 KotlinVMware Tanzu
 
Kotlin: Why Do You Care?
Kotlin: Why Do You Care?Kotlin: Why Do You Care?
Kotlin: Why Do You Care?intelliyole
 
A TypeScript Fans KotlinJS Adventures
A TypeScript Fans KotlinJS AdventuresA TypeScript Fans KotlinJS Adventures
A TypeScript Fans KotlinJS AdventuresGarth Gilmour
 
Kotlin for Android devs
Kotlin for Android devsKotlin for Android devs
Kotlin for Android devsAdit Lal
 
Kotlin / Android Update
Kotlin / Android UpdateKotlin / Android Update
Kotlin / Android UpdateGarth Gilmour
 
Rapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and KtorRapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and KtorTrayan Iliev
 
2#Kotlin programming tutorials(data types and hello world)
2#Kotlin programming tutorials(data types and hello world)2#Kotlin programming tutorials(data types and hello world)
2#Kotlin programming tutorials(data types and hello world)Naveen Davis
 
Разработка кросс-платформенного кода между iPhone &lt; -> Windows с помощью o...
Разработка кросс-платформенного кода между iPhone &lt; -> Windows с помощью o...Разработка кросс-платформенного кода между iPhone &lt; -> Windows с помощью o...
Разработка кросс-платформенного кода между iPhone &lt; -> Windows с помощью o...Yandex
 
Kotlin for Android Developers - 3
Kotlin for Android Developers - 3Kotlin for Android Developers - 3
Kotlin for Android Developers - 3Mohamed Nabil, MSc.
 

Similar to Writing Kotlin Multiplatform libraries that your iOS teammates are gonna love (20)

Introduction to Koltin for Android Part I
Introduction to Koltin for Android Part I Introduction to Koltin for Android Part I
Introduction to Koltin for Android Part I
 
Koin Quickstart
Koin QuickstartKoin Quickstart
Koin Quickstart
 
Being Expressive in Code
Being Expressive in CodeBeing Expressive in Code
Being Expressive in Code
 
MOOC_PRESENTATION_KOTLIN[1].pptx
MOOC_PRESENTATION_KOTLIN[1].pptxMOOC_PRESENTATION_KOTLIN[1].pptx
MOOC_PRESENTATION_KOTLIN[1].pptx
 
moocs_ppt.pptx
moocs_ppt.pptxmoocs_ppt.pptx
moocs_ppt.pptx
 
Having Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo SurabayaHaving Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo Surabaya
 
Android & Kotlin - The code awakens #01
Android & Kotlin - The code awakens #01Android & Kotlin - The code awakens #01
Android & Kotlin - The code awakens #01
 
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
 
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
Multiplatform shared codebase with Kotlin/Native - UA Mobile 2019
 
Why Spring <3 Kotlin
Why Spring <3 KotlinWhy Spring <3 Kotlin
Why Spring <3 Kotlin
 
Kotlin: Why Do You Care?
Kotlin: Why Do You Care?Kotlin: Why Do You Care?
Kotlin: Why Do You Care?
 
A TypeScript Fans KotlinJS Adventures
A TypeScript Fans KotlinJS AdventuresA TypeScript Fans KotlinJS Adventures
A TypeScript Fans KotlinJS Adventures
 
Kotlin for Android devs
Kotlin for Android devsKotlin for Android devs
Kotlin for Android devs
 
Kotlin / Android Update
Kotlin / Android UpdateKotlin / Android Update
Kotlin / Android Update
 
Java vs kotlin
Java vs kotlinJava vs kotlin
Java vs kotlin
 
VR Workshop #2
VR Workshop #2VR Workshop #2
VR Workshop #2
 
Rapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and KtorRapid Web API development with Kotlin and Ktor
Rapid Web API development with Kotlin and Ktor
 
2#Kotlin programming tutorials(data types and hello world)
2#Kotlin programming tutorials(data types and hello world)2#Kotlin programming tutorials(data types and hello world)
2#Kotlin programming tutorials(data types and hello world)
 
Разработка кросс-платформенного кода между iPhone &lt; -> Windows с помощью o...
Разработка кросс-платформенного кода между iPhone &lt; -> Windows с помощью o...Разработка кросс-платформенного кода между iPhone &lt; -> Windows с помощью o...
Разработка кросс-платформенного кода между iPhone &lt; -> Windows с помощью o...
 
Kotlin for Android Developers - 3
Kotlin for Android Developers - 3Kotlin for Android Developers - 3
Kotlin for Android Developers - 3
 

Recently uploaded

Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackVICTOR MAESTRE RAMIREZ
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noidabntitsolutionsrishis
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanyChristoph Pohl
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfLivetecs LLC
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....kzayra69
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 

Recently uploaded (20)

Cloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStackCloud Management Software Platforms: OpenStack
Cloud Management Software Platforms: OpenStack
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte GermanySuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
SuccessFactors 1H 2024 Release - Sneak-Peek by Deloitte Germany
 
How to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdfHow to Track Employee Performance A Comprehensive Guide.pdf
How to Track Employee Performance A Comprehensive Guide.pdf
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 

Writing Kotlin Multiplatform libraries that your iOS teammates are gonna love

  • 1. Writing Kotlin Multiplatform libraries that your iOS teammates are gonna love André Oriani
  • 2. Disclaimer My presentation, comments and opinions are provided in my personal capacity and not as a representative of Walmart. They do not reflect the views of Walmart and are not endorsed by Walmart.
  • 3. TWITTER.COM/AORIANI ANDROIDDEV.SOCIAL/@AORIANI MEDIUM.COM/@AORIANI GITHUB.COM/AORIANI LINKEDIN.COM/IN/AORIANI • Italian🇮🇹-Brazilian🇧🇷 • Working professionally with Android since its first public releases. • Worked on the development of the Homescreen and the Instant Messaging app for the very first Motorola Android phones, including the DROID series. • Currently tech lead at Walmart in the Silicon Valley André Oriani
  • 6. In order to accomplish our secret goal of complete domination of the world of mobile development…
  • 7. Kotlin and Swift are so similar…
  • 8.
  • 10. Presentation Format //KOTLIN API data class Person( val name: String, val age: Int ) //EXPORTED OBJ-C HEADER __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Person"))) @interface SharedPerson : SharedBase - (instancetype)initWithName:(NSString *)name age:(int32_t)age __attribute__((swift_name("init(name:age:)"))) __attribute__((objc_designated_initializer)); - (SharedPerson *)doCopyName:(NSString *)name age:(int32_t)age __attribute__((swift_name("doCopy(name:age:)"))); - (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); - (NSUInteger)hash __attribute__((swift_name("hash()"))); - (NSString *)description __attribute__((swift_name("description()"))); @property (readonly) int32_t age __attribute__((swift_name("age"))); @property (readonly) NSString *name __attribute__((swift_name("name"))); @end // SWIFT CLIENT CODE let keanuReeves = Person(name: "Keanu Charles Reeves", age: 59) [OBJ-C]
  • 11. Presentation Format //KOTLIN API data class Person( val name: String, val age: Int ) //HEADER “TRANSLATED” TO SWIFT public class Person : KotlinBase { public init(name: String, age: Int32) open func doCopy(name: String, age: Int32) -> Person open func isEqual(_ other: Any?) -> Bool open func hash() -> UInt open func description() -> String open var age: Int32 { get } open var name: String { get } } // Which one is which? let keanuReeves = Person(name: "Keanu Charles Reeves", age: 59)
  • 13. Name Clashing THE PROBLEM • Kotlin • Multiple levels of namespace due to packages; • Method signature is the method name plus parameter types. • Objective-C • No support to namespaces; • Method signature is the method name plus parameter names. • Swift • One level of namespace: the exported framework; • Method signature is the method name plus parameter names.
  • 14. Classes with same name but different packages package io.aoriani.network class Item ... package io.aoriani.models class Item public class Item : KotlinBase { public init() } public class Item_ : KotlinBase { public init() } // Which one is which? let item1 = Item() let item2 = Item_() THE PROBLEM
  • 15. Classes with same name but different packages package io.aoriani.network @ObjCName("ItemResponse") class Item ... package io.aoriani.models @ObjCName("ItemModel") class Item public class ItemResponse : KotlinBase { public init() } public class ItemModel : KotlinBase { public init() } let itemDomainModel = ItemModel() let itemNetworkResponse = ItemResponse() THE SOLUTION
  • 16. Overloading with params named the same fun sort(data: List<Int>){} fun sort(data: Map<String, Int>){} public class ExampleKt : KotlinBase { open class func sort(data: [KotlinInt]) open class func sort(data_ data: [String : KotlinInt]) } ExampleKt.sort(data: [1, 2, 3]) ExampleKt.sort(data_: ["A" : 1, "B": 2]) THE PROBLEM
  • 17. Overloading with params named the same fun sort(listOfInts: List<Int>){} fun sort(mapOfStringToInt: Map<String, Int>){} public class ExampleKt : KotlinBase { open class func sort(listOfInts: [KotlinInt]) open class func sort(mapOfStringToInt: [String : KotlinInt]) } ExampleKt.sort(listOfInts: [1, 2, 3]) ExampleKt.sort(mapOfStringToInt: ["A" : 1, "B": 2]) THE SOLUTION
  • 18. Interface fields, same name, different types interface Person { val id: String } interface Robot { val id: Long } public protocol Person { var id: String { get } } public protocol Robot { var id_: Int64 { get } } // Only possible because Konan renamed field for Robot class Android: Person, Robot { let id: String = "" let id_: Int64 = 0 } THE PROBLEM
  • 20. Collection with types conforming to protocol interface Product class Consumer { fun consume(products: Set<Product>) {} } public protocol Product {} public class Consumer : KotlinBase { public init() open func consume(products: Set<AnyHashable>) } // Compiles fine in Swift let consumer = Consumer() let fakeProducts: Set<Int> = [1, 2, 3] consumer.consume(products: fakeProducts) THE PROBLEM
  • 21. Collection with types conforming to protocol THE PROBLEM Set<Product> NSSet<id<Product>> Set<T> where T: Hashable [OBJ-C] MAPS TO BRIDGES TO • Swift Set requires elements conforming to protocol Hashable; • Kotlin Interfaces are exported as Objetive-C protocols; • Objective-C protocols cannot conform to other protocols, let alone Swift Protocols, thus the argument type is erased to AnyHashable; • All KMP Classes inherit from NSObject which conforms to Hashable;
  • 22. Collection with types conforming to protocol abstract class Product class Consumer { fun consume(products: Set<Product>) {} } open class Product : KotlinBase { public init() } public class Consumer : KotlinBase { public init() open func consume(products: Set<Product>) } let consumer = Consumer() let fakeProducts: Set<Int> = [1, 2, 3] // Error: Cannot convert value of type 'Set<Int>' to expected argument type 'Set<Product>' //consumer.consume(products: fakeProducts) let realProducts: Set<Product> = [Product(), Product()] consumer.consume(products: realProducts) THE SOLUTION
  • 23. Values classes @JvmInline value class PostalCode(private val code: String) class Letter(val zipcode: PostalCode) public class Letter : KotlinBase { public init(zipcode: Any) open var zipcode: Any { get } } //This compiles fine let letter = Letter(postalCode: UIViewController()) THE PROBLEM
  • 25. Interface extensions interface Person { val firstName: String val lastName: String } fun Person.fullName() = "$firstName $lastName" public protocol Person { var firstName: String { get } var lastName: String { get } } public class ExampleKt : KotlinBase { open class func fullName(_ receiver: Person) -> String } class SwiftPerson: Person {…} let swiftPerson = SwiftPerson(firstName: "Bruce", lastName: "Wayne") // Java déjà vu? print(ExampleKt.fullName(swiftPerson)) THE PROBLEM
  • 26. Class extensions abstract class Person( val firstName: String, val lastName: String ) fun Person.fullName() = "$firstName $lastName" open class Person : KotlinBase { public init(firstName: String, lastName: String) open var firstName: String { get } open var lastName: String { get } } extension Person { open func fullName() -> String } class SwiftPerson: Person {…} let swiftPerson = SwiftPerson(firstName: "Bruce", lastName: "Wayne") print(swiftPerson.fullName()) THE SOLUTION
  • 27. Filling the gaps between Kotlin and Swift class MyRange { fun isInRange(value: Int, range: IntRange) = value in range } fun test(){ MyRange().isInRange(1, -7..6) } public class MyRange : KotlinBase { public init() open func isInRange(value: Int32, range: KotlinIntRange) -> Bool } let myRange = MyRange() // This is a bit clumsy myRange.isInRange(value: 1, range: KotlinIntRange(start: -1, endInclusive: 2)) THE PROBLEM
  • 28. Filling the gaps between Kotlin and Swift class MyRange { @ShouldRefineInSwift fun isInRange(value: Int, range: IntRange) = value in range } public class MyRange : KotlinBase { public init() } let myRange = MyRange() myRange.isRange(value: 2, range: -1...2) extension MyRange { func isInRange(value value: Int32, range range: ClosedRange<Int32>) -> Bool { return __is(inRangeValue: value, range: KotlinIntRange(start: range.lowerBound, endInclusive: range.lowerBound)) } } THE SOLUTION
  • 29. Providing only platform specific code import io.ktor.http.Url import java.net.URL fun Url.toURL(): URL = URL(this.toString()) let url = Service.shared.baseUrl.toURL() let session = URLSession(configuration: .default) session.dataTask(with: url) { ... extension Ktor_httpUrl { open func toURL() -> URL } import io.ktor.http.Url import platform.Foundation.NSURL fun Url.toURL(): NSURL = NSURL(string = toString()) androidMain iosMain THE SOLUTION
  • 30. Default arguments fun compareString(a: String, b: String, ignoreCase: Boolean = false): Boolean { return a.equals(b, ignoreCase = ignoreCase) } public class ExampleKt : KotlinBase { open class func compareString(a: String, b: String, ignoreCase: Bool) -> Bool } let result1 = ExampleKt.compareString(a: "HELLO", b: "hello", ignoreCase: true) // Compile error: Missing argument for parameter 'ignoreCase' in call let result2 = ExampleKt.compareString(a: "Roma", b: "Londres") THE PROBLEM
  • 32. Exceptions class Bomb { fun explode() : Unit = throw IOException(”Bomb has detonated") } public class Bomb : KotlinBase { public init() open func explode() } func defuseBomb() { let bomb = Bomb() do { bomb.explode() } catch { // 'catch' block is unreachable because no errors are thrown in 'do' block print("Bomb disarmed!") } } THE PROBLEM
  • 33. Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError. It is considered unexpected and unhandled instead. Program will be terminated. Uncaught Kotlin exception: io.ktor.utils.io.errors.IOException: Bomb has detonated at 0 shared 0x103e6ec95 kfun:kotlin.Exception#<init>(kotlin.String?;kotlin.Throwable?){} + 133 (/opt/buildAgent/work/f43969c6214a19e7/kotlin/kotlin-native/runtime/src/main/kotlin/kotlin/Exceptions.kt:25:63) at 1 shared 0x1040c6de7 kfun:io.ktor.utils.io.errors.IOException#<init>(kotlin.String;kotlin.T hrowable?){} + 119 (/opt/buildAgent/work/8d547b974a7be21f/ktor-io/posix/src/io/ktor/utils/io/errors/IOException.kt:4:58) at 2 shared 0x1040c6e4d kfun:io.ktor.utils.io.errors.IOException#<init>(kotlin.String){} + 93 (/opt/buildAgent/work/8d547b974a7be21f/ktor-io/posix/src/io/ktor/utils/io/errors/IOException.kt:5:50) at 3 shared 0x103dc6591 kfun:io.aoriani.kmpapp.Bomb#explode(){} + 145 at 4 shared 0x103dc8c54 objc2kotlin_kfun:io.aoriani.kmpapp.Bomb#explode(){} + 148 at 5 iosApp 0x1027a6c59 $s6iosApp10defuseBombyyF + 57 (/Users/aoriani/Development/Multiplatform/KmpApp/iosApp/iosApp/ContentView.swift:33:14) at 6 iosApp 0x1027a6f5d $s6iosApp11ContentViewVACycfC + 141 (/Users/aoriani/Development/Multiplatform/KmpApp/iosApp/iosApp/ContentView.swift:10:0) at 7 iosApp 0x1027a67eb $s6iosApp6iOSAppV4bodyQrvgAA11ContentViewVyXEfU_ + 27 (/Users/aoriani/Development/Multiplatform/KmpApp/iosApp/iosApp/iOSApp.swift:7:4) at 8 SwiftUI 0x105435a01 get_witness_table 7SwiftUI4ViewRzlAA15ModifiedContentVyxAA25ComplicationIdiomModifierVGAaBHPxAaBHD1__AfA0cH0HPyHCHCTm + 60924 at 9 iosApp 0x1027a66cc $s6iosApp6iOSAppV4bodyQrvg + 156 (/ CRASH!
  • 34. Exceptions class Bomb { @Throws(IOException::class) fun explode() : Unit = throw IOException("Error") } public class Bomb : KotlinBase { public init() open func explode() throws } func defuseBomb() { let bomb = Bomb() do { try bomb.explode() } catch let error as NSError { switch error.kotlinException { case let ioe as Ktor_ioIOException: print("Bomb defused: (ioe.message ?? "")") default: print("Sorry could not defuse!") } } } - (BOOL)explodeAndReturnError:(NSError * _Nullable * _Nullable)error __attribute__((swift_name(”explode()")) ); [OBJ-C] Konan Xcode Bridging THE SOLUTION
  • 36. Kotlin Enums vs Swift Enums enum class Result { LOADING, SUCCESS, FAILURE } public class Result : KotlinEnum<Result> { open class var loading: Result { get } open class var success: Result { get } open class var failure: Result { get } open class func values() -> KotlinArray<Result> open class var entries: [Result] { get } } func testEnum() { let result: Result = .failure switch result { case .loading: print("Loading") case .success: print("Success") case .failure: print("Failed") default: print("I will never be called") } } THE PROBLEM
  • 37. Kotlin Sealed Class vs Swift Enums sealed class Result { data object Loading: Result() class Success(val value: String): Result() class Failure(val throwable: Throwable): Result() } open class Result : KotlinBase {} extension Result { public class Loading : Result { public convenience init() open class var shared: Result.Loading { get } open func isEqual(_ other: Any?) -> Bool open func hash() -> UInt open func description() -> String } public class Failure : Result { public init(throwable: KotlinThrowable) open var throwable: KotlinThrowable { get } } public class Success : Result { public init(value: String) open var value: String { get } } } func testSealedClass() { let result: Result = Result.Loading.shared switch result { case is Result.Loading: print("loading") case let success as Result.Success: print("Success: (success.value)") case let failure as Result.Failure: print("Failure: (failure.throwable.message ?? "")") default: print("I will never be called") } } THE SOLUTION
  • 38. Kotlin Sealed Class vs Swift Enums func testWrappedSealedClass() { if let result = SwiftResult(Result.Loading.shared) { switch result { case .loading: print("Loading") case let .success(value): print("Success: (value)") case let .failure(throwable): print("Failure: (throwable.message ?? "")") } } } enum SwiftResult { case loading case success(value: String) case failure(throwable: KotlinThrowable) } extension SwiftResult { init?(_ result: Result) { switch result { case is Result.Loading: self = .loading case let success as Result.Success: self = .success(value: success.value) case let failure as Result.Failure: self = .failure(throwable: failure.throwable) default: return nil } } } Libraries that can create the wrapper for you: • MOKO KSwift • Touchlab SKIE THE SOLUTION
  • 40. Generics @property (nonatomic,strong) NSArray *arrayOfUrls; @property (nonatomic,strong) NSDictionary *myDictNameToAge; @property (nonatomic,strong) NSSet *setOfStrings; open var arrayOfUrls: [Any] open var myDictNameToAge: [AnyHashable : Any] open var setOfStrings: Set<AnyHashable> @property (nonatomic,strong) NSArray<NSURL *> *arrayOfUrls; @property (nonatomic,strong) NSDictionary<NSString *, NSNumber *> *myDictNameToAge; @property (nonatomic,strong) NSSet<NSString *> *setOfStrings; open var arrayOfUrls: [URL] open var myDictNameToAge: [String : NSNumber] open var setOfStrings: Set<String> [OBJ-C] [OBJ-C] Before Lightweight Generics After Lightweight Generics
  • 41. Generics : Protocols interface Cat<T> { val data: T } public protocol Cat { var data: Any? { get } }
  • 42. Generics : Classes class Dog<T>(val data: T) // Bounded Type Parameter class Bird<T: Number>(val data: T) public class Dog<T> : KotlinBase where T : AnyObject { public init(data: T?) open var data: T? { get } } //Bounded Typed Parameter public class Bird<T> : KotlinBase where T : AnyObject { public init(data: T) open var data: T { get } }
  • 43. Generics : Methods & Functions interface MyInterface class MethodTest { fun <T: Number> eat(value: T): Int = value.toInt() fun <T: MyInterface> drink(value: T) = a } public protocol MyInterface {} public class MethodTest : KotlinBase { public init() open func eat(value: Any) -> Int32 open func drink(value: MyInterface) -> MyInterface }
  • 45. Coroutines class SuspendFunctions { suspend fun generateInteger(): Int { delay(1_000) return 1 } } public class SuspendFunctions : KotlinBase { public init() open func generateInteger(completionHandler: @escaping (KotlinInt?, Error?) -> Void) // After Swift 5.5 we also have open func generateInteger() async throws -> KotlinInt } func usingTask() { let task = Task { @MainActor in do { let object = SuspendFunctions() let result = try await object.generateInteger() print("Result: (result)") } catch { print("We had an error: (error)") } } // This doesn't do what do you think it would do task.cancel() } THE PROBLEM
  • 46. Flows class Flows { fun generateIntegers(): Flow<Int> { return flowOf(1,2,3) } } public class Flows : KotlinBase { public init() open func genenerateIntegers() -> Kotlinx_coroutines_coreFlow } THE PROBLEM
  • 47. KMP-NativeCouroutines class CoroutinesAndFlows { @NativeCoroutineScope internal val scope = MainScope() @NativeCoroutines suspend fun randomInt(): Int { delay(1_000); return (1..10).random() } @NativeCoroutines fun flowOfInt(value: Int): Flow<Int> { return flowOf(value) } } THE SOLUTION
  • 48. KMP-NativeCouroutines: Coroutine import KMPNativeCoroutinesAsync func usingNativeSuspend() { let task = Task { let object = CoroutinesAndFlows() do { let v = try await asyncFunction(for: object.randomInt()) } catch { print("We met an error: (error)") } } //Cancels Swift task and Kotlin Job task.cancel() } THE SOLUTION
  • 49. KMP-NativeCouroutines: Flow import Combine import KMPNativeCoroutinesCombine func usingNativeFlow() { let object = CoroutinesAndFlows() let publisher = createPublisher(for: object.flowOfInt(value: 3)) let cancellable = publisher.sink { completion in switch completion { case .finished: print("Flow has finished") case let .failure(err): print("Flow terminated with error (err)") } } receiveValue: { value in print("Received (value)") } // Cancels Kotlin Flow and Combine Publisher cancellable.cancel() } THE SOLUTION
  • 50. And if you found everything too complicated… Just use Compose Multiplatform!😁
  • 51. Acknowledgement • Thomas Hultgren • Daniel Youn • Paolo Rotolo • John O’Reilly • Pamela Hill • Better Programming • Towards Devs • ProAndroidDev • Android Weekly • Kotlin Weekly
  • 52. &

Editor's Notes

  1. Disclaimer My presentation, comments and opinions are provided in my personal capacity and not as a representative of Walmart. They do not reflect the views of Walmart and are not endorsed by Walmart.
  2. Hi, I am André Oriani Sono italo-brasiliano perciò questo accento un po’ diverso. I work with Android since their very first public releases, because at Motorola at that time, so I worked on the development of their very first Android devices. Currently I am a tech lead for Walmart in California.
  3. If there any slide you want to take a screenshot, this is the one. This whole talk is based on series of articles I wrote about KMP and Swift, so all the content for this talk is there.
  4. Kotlin multiplatform is awesome! Who here has ever written an iOS app? Now with some knowledge of Compose,Ktor, and some other KMP libraries, you can an iOS app, an Web app and even a Desktop app from the same code with little to no knowledge of those platforms.
  5. However In order to accomplish our secret goal of complete domination of the world of mobile development we need to bring the iOS developers to our side. And how can we do that? By writing Kotling Multiplatform APIs that are so good that they won’t even notice that they were written in Kotlin in the first place. And that is the whole topic of this presentation.
  6. But you can ask me: “André, Kotlin and Swift look so similar… What is that so hard?
  7. Well, If you ever visit San Francisco and cross the Golden Gate Bridge you gonna have to pay the toll. And The toll to cross the bridge between Kotlin and Swift is called Objective-C.
  8. The Kotlin API you wrote is compiled by Konan, the Kotlin Native Compiler, and exported as an Objective-C Framework. Then Xcode bridges the Objective-C framework to Swift. Pay attention, Xcode is the the one that exposes your Kotlin API to Swift by doing the bridging work. That is were the problem resides. You can have features in Kotlin that are similar to Features in Swift, but because they are missing in Objective, they are lost in translation. But why Objective-C? Many reasons, One is that Swift Binary interface only became stable quite recently. Another is similar to using Java over Kotlin. It makes your API available to a wider audience.
  9. So this is going to be the format for this presentation. First I am gonna propose a Kotlin API Then I show how that API is exported to Objective-C And finally I am gonna show you some Swift client code using the API. But as you can see the Objective-C code can be quite verbose and hard to read, so I am gonna be providing the translated version to Swift
  10. Much better, right ? So pay attention to the icons: Kotlin icon for KMP API code Swift orange for the exported API And SwiftUI blue for the swift client code.
  11. The first problem we will deal with is Name clashing. So whenever you see extra underscore in your exported API is because some name clashing happened.
  12. Why do those clashes happen ? First, whereas Kotlin has multiple levels of namespace due to the ability of nesting of packages, Swift has only one level-– the exported framework– and Objective has not support to namespaces at all. So all your KMP classes will be thrown in a single namespace. Second, whereas the method signature in Kotlin is the method name plus the parameter types, both Swift and Objective use the method name plus the parameter names. So if you have overloaded functions with the same parameter names they will clash
  13. Here we have two classes with the same name, Item, but they belong to different packages, network and models. In Swift, to avoid the clash one of them gets an underscore, but which one ? Is item1 in the example a network object or a model one.
  14. To address such situation we can use the ObjCName annotation which allows us to specify what will be the symbol’s name in Objective-C and Swift. So now it is clear to Swift developers which class is the network model and which one is the domain model.
  15. In this case we have an overloaded function sort but both versions use the same parameter name, data. That is okay in Kotlin because the parameter types are distinct. But it is a problem in Swift, because as I said before the parameter names are used for the method signature so they will clash. Konan, the kotlin native compiler, will avoid the clash by adding an underscore again. There are two ways to solve this problem: Either you can use the ObjcName annotation again…
  16. or you can just use better names for the parameters.
  17. This case is a bit curious. We have two interfaces, Person and Robot, and both have the field id. But id is a string for Person and a long for Robot. In Kotlin would be impossible to have an Android class that implement both interfaces. The Kotlin JVM compiler would complain about the id fields having same name but conflicting types. However Konan tries to be proactive here and adds an underscore, nut to which one of them? I don’t know, it is unpredictable. If you are the owner of the interfaces you can use the ObjCName annotation or renamed one of the id fields
  18. Now let’s talk about disappearing types. Here, I am goona covering cases here in which Swift will use a complete different type from the one you defined in your Kotlin API
  19. Here we defined an interface Product and a class Consumer that has a method consume that take a Set of Products. In Swift , our interface Product becomes the protocol Product. Our class Consumer is still a class, but the consume method now takes a set of AnyHashables… Where did those AnyHashable come from ? And if you look at the example you can define an set of integers and use with the consume method, because integers are, of course, Hashable. It will compile fine, but it won’t work at runtime. So what is going on here ?
  20. So when you define a Set in your Kotlin API, it will be mapped to an Objective-C NSets. Objective-C NSSets are bridged to Swift Sets. But a swift Set requires its elements to conform to the protocol Hashable, a swift protocol. The problem is that Kotlin Interfaces are exported as Objetive-C protocols.. And an Objective-C protocol cannot be extend to conform to other protocols, let alone Swift Protocols. Therefore our product interface cannot conform to Hashable protocol. Consequently the argument type is erased to AnyHashable. Similar thing will happen with Maps. They will be mapped to NSDictionaries, which will be bridged to Swift Dictionaries. Which in turn will require the type for the key to conform to Hashable. On the other hand All KMP Classes inherit indirectly from KotlinBase which inherits from NSObject. And NSObject which conforms to Hashable; After all all objects in Kotlin have the hashcode method.
  21. Therefore the solution is to use abstract classes. Well, Kotlin interfaces kinda behave like abstract classes with everything abstract. Swift protocols are a bit different because other things beyond classes like swift structs can conform to protocols. So now Product is class in Kotlin and Swift and the consume method now takes a set of Products. And the client can only pass a set of products.
  22. Okay, here we defined an inline class PostalCode, zip code if you are American, and a class letter that takes the Postalcod . But then when you look to the swift interface postalCode has now type Any , which is Swift is really anything, objects, structs, primitive types and what not. You can see on the example that I can even pass an UIViewControler to Letter. In better cases Kotlin will replace it with the boxed type, in this case a string, but the general advice is to avoid inline classes.
  23. Providing convenience. In this section I will talk about case in which the exported API is still usable by Swift developers but it could be improved to become more intuitive to them.
  24. Here we define an interface Person and define the extension fullName. Person is exported as a protocol and when we look to the code generated for the extension it remembers when you use a Kotlin extension from Java, it is, it becomes this static method whose first parameter is a receiver, the interface you are extending The problem here is that Objective-C protocols cannot have extensions. so Konan has to generate that is very similar to what we see from Java.
  25. And if you paid attention to a couple slides ago, you know the solution will be to use a class. And in fact Kotlin extension for classes are exported as extension to Objective-C Classes, so everything works as expected. So we are seeing a trend here, that Objective-C protocols don’t behave the same way we expect Kotlin interfaced to do. But don’t take it bad, Protocols are an essential part of Swift programming.
  26. Both Kotlin have Ranges, but Objective doesn’t, so as I said ranges are lost in translation. So when you API has ranges it is pretty clumsy, akward for swift developers to use it. They have to instance this KotlinInRange object. Wouldn’t be better if they could use ranges as well ?
  27. The trick here is to use the annotation ShouldRefineInSwift. Your method will still be available in Objective-C, but it hidden from Swift because two underscore were prepended to its name. You can see on the first panel on the right that the isInRange method has disappeared in Swift So now we can write a Swift extension that accepts swift ranges. The extension wraps the original method and converts Swift Ranges to Kotlin ranges. So that is the general strategy to fix a gap in the translation that was created by objective-c. We create a swift extension to cover that gap.
  28. Another thing you can is that you don’t need to write everything in common. We can have code that is specific to one platform. For instance here we have an extension to the Ktor’s URL class that converts it to a native URL type. So in the Android Main it converts to a Java net URL. In the iOSMain sourceset it converts to NSURL in Objective, and thanks to Xcode, URL in Swift, that can be used in iOS
  29. Well, I have bad news for you. Default argument in functions won’t work because, guess what ? Because Objective-C doesn’t support them again. So they are also lost in translation. There’s a proposal for having an annotation similar to JVMOverloads, that would generate all the overloads to you, but until that is a reality, you will have to code the overloads yourself. So you see in the example that if I don’t supply the ignoreCase argument it will not compile
  30. Ah, exceptions. Exceptions are curios case . Remember that in Java we have checked and unchecked exceptions. If it is a checked exception, either you must catch or you warn you caller that you may throw an exception thru a throw clause. Kotlin went away with checked exceptions. But Swift still have them, so that leads to interesting results…
  31. Here we have a class bomb with a method that will certainly throw an exception. Our swift developer tries to be very careful to defuse such bomb, it has this nice do-catch-all block around out problematic method. But….
  32. Boom…. I crashes anyway, despite the efforts of the swift developer And the fault was all ours as it is made pretty clear from the error message Function doesn’t have or inherit @Throw annotation and this exception isn't prorogated from Kotlin to Objective-C Swift as NSError. So let’s add the throw annotation and see what that message exactly means.
  33. So I added the Throw annotation to warn that our method may throw an IOException. This time I must do different, I need to show you the exported Objetive-C code. We see that Konan completely changed the method signature. The method is now named explodeAndReturnError and it takes a pointer to pointer to a NSError Object. That is the convention followed by the Cocoa libraries, libraries like Foundation and UIKit that are used for iOS Apps. The conventions is that any method that can generator errors will have a NSError pointer as the last parameter. If that pointer is null , the method succeeded. If not we can inspect the returned NSError object to understand what was the problem. Those methods when bridge to Swift will become methods that can throw an NSError. So we see that in Swift our method explode has throws clause. Now our swift developer can catch the exception as a NSError use the kotlinException extension to recover the original Kotlin Exception
  34. Enum & Sealed classes. Whoever used Swift Enums know that they are the perfect mix between Kotlin enums and sealed. Therefore in order to convince ios devs that KMP is a good idea we must strive for Kotlin enums and sealed classe to behave like Swift enums. Let’s see how they behave.
  35. The problem for Kotlin enums is that Objective-C only has the old C-Style enums, in which every element of enum is mapped to integer constant. However the Konan does a good job of emulating Swift enums by create a class in which every element of the enums is a immutable static instance of the class. We can see on the example that the usage is pretty close to swift enums, but because there’s no way for swift to know that loading, success and failures are the only possibility of our “enum class”, Switch in Swift must be exhaustive, we need to add a useless default clause, that never would be hit.
  36. And things are not better for Sealed classes. Sealed classes are the closest thing to swift enums,. But Swift we see our sealed classes as a ordinary class hierarchy . We can see in the example testSealedClass that our Swift developer will have a hard time trying to downcast to the right class. So what is the solution here? We already saw when there is a feature in both kotlin and swift but is missing in objective we will need to write some swift code
  37. The solution is write a Swift enum that will wrap our Kotlin enum or sealed classes. The enum Swift will also have loading, success, and failures elements. The craziest thing I learned about Swift is that you can define new constructor to a class thru extensions and that new constructor can even return null, something unthinkable for Kotlin. So we define a constructor that will convert our sealed class to the swift enum. Now we can see on the right panel that is business as usual for our iOS colleague. But do you really need to write Swift code? Well, there are libraries that can do that for you: Moko Kswift and Touchlab SKIE.
  38. Generics were not supported by Objective-C until 2015, when Apple introduced lightweight Generics, with the sole purpose of improving the interoperability between objective-c collections and swift collections.
  39. So Before Generics you could declare an NSArray but you could not tell what that NSArray contains. It would be mapped in swift to a List of Any, and the iOS developer would have the hurdle of forcibly downcast it to the right type. Similar thing to other collection like NSDictionaries and NSSet After Generics of course you can specify the contained type. But because the sole goal of Lightweight generics was improve the interoperability of collection classes, generics are only supported by classes
  40. So we see that for GenericInterace, data has type Any in Swift because generics are not supported for protocols. Generic class is fine, but in BoundClass, we see that the bounds are ignore. The bound for the generic type is Number in Kotlin, but still AnyObject in Swift. Methods and functions do not support generics, but there’s something curious. For types defined by us, Konan does some type erasure. We can on the right panel that the argument’s type of the identity function is mapped to MyInterface, a type defined by us. Where as for the toInt function, uses Any instead of Number, a type defined by java
  41. So we see that for GenericInterace, data has type Any in Swift because generics are not supported for protocols. Generic class is fine, but in BoundClass, we see that the bounds are ignore. The bound for the generic type is Number in Kotlin, but still AnyObject in Swift. Methods and functions do not support generics, but there’s something curious. For types defined by us, Konan does some type erasure. We can on the right panel that the argument’s type of the identity function is mapped to MyInterface, a type defined by us. Where as for the toInt function, uses Any instead of Number, a type defined by java
  42. So we see that for GenericInterace, data has type Any in Swift because generics are not supported for protocols. Generic class is fine, but in BoundClass, we see that the bounds are ignore. The bound for the generic type is Number in Kotlin, but still AnyObject in Swift. Methods and functions do not support generics, but there’s something curious. For types defined by us, Konan does some type erasure. We can on the right panel that the argument’s type of the identity function is mapped to MyInterface, a type defined by us. Where as for the toInt function, uses Any instead of Number, a type defined by java
  43. So suspend fuctions are exported to Object-C as function with a completion handler which is the fancy name apple gave to callbacks. Swift since version 5.5 supports asynchronous functions with async/await. CompletionHandler in Objective-C are mapped to async fucntions in Swift. So that is why we see two genInteger methods in swift, the original from Objective-C using callbacks and the bridged async one. We don’t want to use the callback version because we don’t want to create the pyramid of doom , the callback hell. We see on the right panel that using the async version is pretty similar to using coroutines . You have to create a Task which primity to a couroutine scope and a Job. MainAction is like the main dispatcherYou have to use await keyword to start the async function witch is pretty similar to launch and async methods. But the gotcha here is that canceling the task won’t cancel the coroutine. Remember the async method was generated by xcode who has no idea that you are using kotlin coroutines.
  44. And things for flow . Beside not being able to cancel the collection of the flow. Flow, SharedFlow, Stateflow are all interfaces. And just learned that interfaces pare mapped to protocols which don’t support generics. So wee that in Swift genIntegers just returns a flow not a flow of integer like in kotlin. So even if your swift developer tries to use genInterger, she will have to forcebly cast every emission from Any to Integer. So how we fix the mess. The solution is quite convoluted. On the kotlin side you need to create a wrapper that allow you to start the coroutine or flow and to cancel it. On the Swift side you need methods that know how to open the wrapper and they propagate the task cancellation, so we can stop the flow or coroutine. Fortunely there are libraries that do that for your. I am gonna talk about KMP-NativeCoroutine which recently received a grant from Kotlin Foundation. Rick the maintainer of the library is awesome. I submitted a bug report and he fixed and released a new version in matter of couple hours.
  45. KMP-NativeCouroutines is easy to use. All you you need to do is to annotate the couroutine scope you want to use with the NativeCouroutineScope and annotate your suspend functions and flow with the NativeCoroutines annotation . The NativeCoroutines annotation does two things: * It prevents the annoted suspend function and flow from being exported to objective-c much like the hiddenfromobjectivec annotation. * During the annotation processing phase with KSP it will generation ios specific extensions. We can see the generated code on the right panel. See that the extensions have the ObjetiveCName annotation, so they are effectively replacing you original method with wrapped versions. NativeSuspend and NativeFlow the wrapper types, although they look generic classes, they are actually types alias for a function that receive functions that as arguments. It sounds complicates because it is as we will see in the next slide in which I show the exported swift code.
  46. Despite the complicated return type we can see on the panel at the right that things remain quite the same for our iOS developers. All they need to do is to import the KMPNativeCoroutinesAsync framework to be able use the asyncFunction function that will start our coroutine and stablish the connection between the Swift task and the Kotlin coroutine. So now when the task is cancelled the coroutine will be cancelled as well.
  47. KMP-NativeCoutines has a framework flavor that allows to convert our flow to RxSwift. But the same way that we are moving from RxJava to Flow, swift devs are moving from RxSwift to Combine, a publisher-subscriber framework. Thus I am gonna show how to use KMP-NativeCoutines with Combine. Our ios Dev then need to import Combine and KMPNativeCouroutineCombine. The function createPublisher converts our flow to the Swift pubisher. Then he can call sink on the publisher that is the same thing as collect or subscribe. And again, canceling the task will cancel the flow.
  48. And if you found everything too complicate just use Compose Multiplatform. You can write an entire iOS app just using Kotin and forget about the Swift developers.