Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Effective Swift-Kotlin Interoperability (KotlinConf 2019)

Effective Swift-Kotlin Interoperability (KotlinConf 2019)

Over the past year, Multiplatform Fever has swept the world with the promise of code sharing between iOS and Android codebases. But how does it *really* work? What compromises have to be made by both platforms in order to achieve this goal?

This talk will make a deep comparison of Swift vs. Kotlin, you’d need a deep understanding of both languages

Some topics that would be covered:
* How do Swift consumers interact with Kotlin code (for example, generics)
* What problems does ObjC bring to the table?
* Fundamental differences between Swift and Kotlin today (value types/structures vs data classes, enums with associated values vs sealed classes)
* performance implications (build and runtime)
* current problems + best practices to avoid them

Attendees will leave this talk with a better understanding of what problems still exist, yet feel encouraged to start dabbling in their current codebases.

Video: https://youtu.be/Ij9guIZ-vBY?t=22180

6c8b509fe5422470d148c2c4cf2eb4b0?s=128

John Rodriguez

December 06, 2019
Tweet

Transcript

  1. Copenhagen Denmark EFFECTIVE SWIFT- KOTLIN INTEROPERABILITY JOHN RODRIGUEZ @jrodbx

  2. None
  3. None
  4. None
  5. @objc

  6. None
  7. None
  8. None
  9. include ':app' settings.gradle settings.gradle

  10. include ':app' include ':SharedCode' settings.gradle

  11. plugins { kotlin("multiplatform") } kotlin { … iOSTarget("ios") { binaries

    { framework { baseName = "ESKIOFramework" } } } jvm("android") sourceSets["commonMain"].dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-common") SharedCode/build.gradle.kts
  12. } } jvm("android") sourceSets["commonMain"].dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-common") } sourceSets["androidMain"].dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib")

    } } val packForXcode by tasks.creating(Sync::class) { val targetDir = File(buildDir, "xcode-frameworks") /// selecting the right configuration for the iOS /// framework depending on the environment /// variables set by Xcode build val mode = System.getenv("CONFIGURATION") ?: "DEBUG" val framework = kotlin.targets .getByName<KotlinNativeTarget>("ios") SharedCode/build.gradle.kts
  13. implementation("org.jetbrains.kotlin:kotlin-stdlib") } } val packForXcode by tasks.creating(Sync::class) { val targetDir

    = File(buildDir, "xcode-frameworks") … val framework = kotlin.targets .getByName<KotlinNativeTarget>("ios") .binaries.getFramework(…) dependsOn(framework.linkTask) from({ framework.outputDirectory }) into(targetDir) … } tasks.getByName("build").dependsOn(packForXcode) SharedCode/build.gradle.kts
  14. None
  15. None
  16. None
  17. None
  18. None
  19. None
  20. None
  21. None
  22. None
  23. http://bit.ly/2RvF72h

  24. None
  25. ipa apk

  26. jar framework aar actual/expect expect/actual SharedCode ipa apk

  27. apk ipa jar framework aar aar jar Klib actual/expect expect/actual

    SharedCode actual/expect expect/actual Gradle dependencies
  28. None
  29. object Object class Clazz interface Interface { fun member() {}

    } SharedCode/src/commonMain/kotlin/demoCommon.kt
  30. object Object class Clazz interface Interface { fun member() {}

    } let obj1 = Object() print(address(o: obj1)) let obj2 = Object() print(address(o: obj2)) let class1 = Clazz() print(address(o: class1)) let class2 = Clazz() print(address(o: class2))
  31. object Object class Clazz interface Interface { fun member() {}

    } let obj1 = Object() print(address(o: obj1)) let obj2 = Object() print(address(o: obj2)) let class1 = Clazz() print(address(o: class1)) let class2 = Clazz() print(address(o: class2)) 105553140519616 105553140519616 105553140519264 105553140519072
  32. @interface KotlinBase : NSObject - (instancetype)init __attribute__((unavailable)); + (instancetype)new __attribute__((unavailable));

    + (void)initialize __attribute__((objc_requires_super)); @end; __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Object"))) @interface ESKIOFObject : KotlinBase + (instancetype)object __attribute__((swift_name("init()"))); @end; __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Clazz"))) @interface ESKIOFClazz : KotlinBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); @end; KotliniOS/Frameworks/ESKIOFramework.framework/Headers/ESKIOFramework.h
  33. @interface KotlinBase : NSObject - (instancetype)init __attribute__((unavailable)); + (instancetype)new __attribute__((unavailable));

    + (void)initialize __attribute__((objc_requires_super)); @end; __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Object"))) @interface ESKIOFObject : KotlinBase + (instancetype)object __attribute__((swift_name("init()"))); @end; __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Clazz"))) @interface ESKIOFClazz : KotlinBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); @end; KotliniOS/Frameworks/ESKIOFramework.framework/Headers/ESKIOFramework.h
  34. @interface KotlinBase : NSObject - (instancetype)init __attribute__((unavailable)); + (instancetype)new __attribute__((unavailable));

    + (void)initialize __attribute__((objc_requires_super)); @end; __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Object"))) @interface ESKIOFObject : KotlinBase + (instancetype)object __attribute__((swift_name("init()"))); @end; __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Clazz"))) @interface ESKIOFClazz : KotlinBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); @end; KotliniOS/Frameworks/ESKIOFramework.framework/Headers/ESKIOFramework.h
  35. @interface KotlinBase : NSObject - (instancetype)init __attribute__((unavailable)); + (instancetype)new __attribute__((unavailable));

    + (void)initialize __attribute__((objc_requires_super)); @end; __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Object"))) @interface ESKIOFObject : KotlinBase + (instancetype)object __attribute__((swift_name("init()"))); @end; __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("Clazz"))) @interface ESKIOFClazz : KotlinBase - (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); @end; KotliniOS/Frameworks/ESKIOFramework.framework/Headers/ESKIOFramework.h
  36. fun plusplus(value: Int): Int { return value + 1 }

    SharedCode/src/commonMain/kotlin/demoCommon.kt
  37. fun plusplus(value: Int): Int { return value + 1 }

    let value = 5 DemoCommonKt.plusplus(value: value)
  38. let value = 5 DemoCommonKt.plusplus(value: value) fun plusplus(value: Int): Int

    { return value + 1 } Cannot convert value … 'Int' to … 'Int32' _
  39. __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("DemoCommonKt"))) @interface ESKIOFDemoCommonKt : KotlinBase … + (int32_t)plusplusValue:(int32_t)value __attribute__((swift_name("plusplus(value:)")));

    … @end; KotliniOS/Frameworks/ESKIOFramework.framework/Headers/ESKIOFramework.h
  40. __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("DemoCommonKt"))) @interface ESKIOFDemoCommonKt : KotlinBase … + (int32_t)plusplusValue:(int32_t)value __attribute__((swift_name("plusplus(value:)")));

    … @end; KotliniOS/Frameworks/ESKIOFramework.framework/Headers/ESKIOFramework.h
  41. let value = 5 DemoCommonKt.plusplus(value: value) fun plusplus(value: Int): Int

    { return value + 1 } Cannot convert value … 'Int' to … 'Int32' _
  42. let value = 5 as Int32 DemoCommonKt.plusplus(value: value) fun plusplus(value:

    Int): Int { return value + 1 } Cannot convert value … 'Int' to … 'Int32' _
  43. let value = 5 as Int32 DemoCommonKt.plusplus(value: value) fun plusplus(value:

    Int): Int { return value + 1 } Cannot convert value … 'Int' to … 'Int32' _
  44. fun plusplus(value: Int): Intx{ return valuex+x1 } let value =

    5 as Int32 DemoCommonKt.plusplus(value: value)
  45. fun plusplus(value: Int) = valuex+x1 fun plusplus(value: Long) = value

    + 1 fun plusplus(value: Double) = value + 1 fun plusplus(value: Float) = value + 1 let value = 5 as Int32 DemoCommonKt.plusplus(value: value)
  46. fun plusplus(value: Int) = valuex+x1 fun plusplus(value: Long) = value

    + 1 fun plusplus(value: Double) = value + 1 fun plusplus(value: Float) = value + 1 let iValue = 5 as Int32 let lValue = 5 as Int64 let dValue = 5.0 let fValue: Float = 5.0 DemoCommonKt.plusplus(value__: iValue) DemoCommonKt.plusplus(value___: lValue) DemoCommonKt.plusplus(value: dValue) DemoCommonKt.plusplus(value_: fValue)
  47. __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("DemoCommonKt"))) @interface ESKIOFDemoCommonKt : KotlinBase … + (double)plusplusValue:(double)value __attribute__((swift_name("plusplus(value:)")));

    + (float)plusplusValue_:(float)value __attribute__((swift_name("plusplus(value_:)"))); + (int32_t)plusplusValue__:(int32_t)value __attribute__((swift_name("plusplus(value__:)"))); + (int64_t)plusplusValue___:(int64_t)value __attribute__((swift_name("plusplus(value___:)"))); … @end; KotliniOS/Frameworks/ESKIOFramework.framework/Headers/ESKIOFramework.h
  48. fun plusplus(value: Int) = valuex+x1 fun plusplus(value: Long) = value

    + 1 fun plusplus(value: Double) = value + 1 fun plusplus(value: Float) = value + 1 let iValue = 5 as Int32 let lValue = 5 as Int64 let dValue = 5.0 let fValue: Float = 5.0 DemoCommonKt.plusplus(value__: iValue) DemoCommonKt.plusplus(value___: lValue) DemoCommonKt.plusplus(value: dValue) DemoCommonKt.plusplus(value_: fValue)
  49. fun plusplus(intValue: Int) = intValue +x1 fun plusplus(longValue: Long) =

    longValue + 1 fun plusplus(doubleValue: Double) = doubleValue + 1 fun plusplus(floatValue: Float) = floatValue + 1 let iValue = 5 as Int32 let lValue = 5 as Int64 let dValue = 5.0 let fValue: Float = 5.0 DemoCommonKt.plusplus(intValue: iValue) DemoCommonKt.plusplus(longValue: lValue) DemoCommonKt.plusplus(doubleValue: dValue) DemoCommonKt.plusplus(floatValue: fValue)
  50. Compromises

  51. Lack of Common Library Support*

  52. None
  53. Okio OkHttp Moshi Wire IO HTTP Client serialization

  54. Okio OkHttp IO HTTP Client serialization Moshi? Wire

  55. Okio OkHttp Moshi? Wire IO HTTP Client serialization DB SqlDelight

  56. None
  57. Avoid UI Layer

  58. None
  59. None
  60. Language Features

  61. enum class City { NEW_YORK, CHICAGO, NAPLES }

  62. __attribute__((swift_name("City"))) @interface ESKIOFCity : ESKIOFKotlinEnum //… @property (class, readonly) ESKIOFCity

    *theNewYork __attribute__((swift_name("theNewYork"))); @property (class, readonly) ESKIOFCity *chicago __attribute__((swift_name("chicago"))); @property (class, readonly) ESKIOFCity *naples __attribute__((swift_name("naples"))); - (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))); - (int32_t)compareToOther:(ESKIOFCity *)other __attribute__((swift_name("compareTo(other:)"))); @end;
  63. __attribute__((swift_name("City"))) @interface ESKIOFCity : ESKIOFKotlinEnum //… @property (class, readonly) ESKIOFCity

    *theNewYork __attribute__((swift_name("theNewYork"))); @property (class, readonly) ESKIOFCity *chicago __attribute__((swift_name("chicago"))); @property (class, readonly) ESKIOFCity *naples __attribute__((swift_name("naples"))); - (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))); - (int32_t)compareToOther:(ESKIOFCity *)other __attribute__((swift_name("compareTo(other:)"))); @end;
  64. enum class City { NEW_YORK, CHICAGO, NAPLES }

  65. enum class City { NEW_YORK, CHICAGO, NAPLES } let city

    = City.chicago switch city { case .chicago: print("Chicago!") case .naples: print("Naples!") case .theNewYork: print("New York!") }
  66. enum class City { NEW_YORK, CHICAGO, NAPLES } let city

    = City.chicago switch city { case .chicago: print("Chicago!") case .naples: print("Naples!") case .theNewYork: print("New York!") } Switch must be exhaustive. _
  67. __attribute__((swift_name("City"))) @interface ESKIOFCity : ESKIOFKotlinEnum //… @property (class, readonly) ESKIOFCity

    *theNewYork __attribute__((swift_name("theNewYork"))); @property (class, readonly) ESKIOFCity *chicago __attribute__((swift_name("chicago"))); @property (class, readonly) ESKIOFCity *naples __attribute__((swift_name("naples"))); - (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))); - (int32_t)compareToOther:(ESKIOFCity *)other __attribute__((swift_name("compareTo(other:)"))); @end;
  68. __attribute__((swift_name("City"))) @interface ESKIOFCity : ESKIOFKotlinEnum //… @property (class, readonly) ESKIOFCity

    *theNewYork __attribute__((swift_name("theNewYork"))); @property (class, readonly) ESKIOFCity *chicago __attribute__((swift_name("chicago"))); @property (class, readonly) ESKIOFCity *naples __attribute__((swift_name("naples"))); - (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal __attribute__((swift_name("init(name:ordinal:)"))); - (int32_t)compareToOther:(ESKIOFCity *)other __attribute__((swift_name("compareTo(other:)"))); @end;
  69. __attribute__((swift_name("KotlinEnum"))) @interface ESKIOFKotlinEnum : KotlinBase <ESKIOFKotlinComparable> - (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal

    __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)); - (int32_t)compareToOther:(ESKIOFKotlinEnum *)other __attribute__((swift_name("compareTo(other:)"))); - (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); - (NSUInteger)hash __attribute__((swift_name("hash()"))); - (NSString *)description __attribute__((swift_name("description()"))); @property (readonly) NSString *name __attribute__((swift_name("name"))); @property (readonly) int32_t ordinal __attribute__((swift_name("ordinal"))); @end;
  70. __attribute__((swift_name("KotlinEnum"))) @interface ESKIOFKotlinEnum : KotlinBase <ESKIOFKotlinComparable> - (instancetype)initWithName:(NSString *)name ordinal:(int32_t)ordinal

    __attribute__((swift_name("init(name:ordinal:)"))) __attribute__((objc_designated_initializer)); - (int32_t)compareToOther:(ESKIOFKotlinEnum *)other __attribute__((swift_name("compareTo(other:)"))); - (BOOL)isEqual:(id _Nullable)other __attribute__((swift_name("isEqual(_:)"))); - (NSUInteger)hash __attribute__((swift_name("hash()"))); - (NSString *)description __attribute__((swift_name("description()"))); @property (readonly) NSString *name __attribute__((swift_name("name"))); @property (readonly) int32_t ordinal __attribute__((swift_name("ordinal"))); @end;
  71. enum class City { NEW_YORK, CHICAGO, NAPLES } let city

    = City.chicago switch city { case .chicago: print("Chicago!") case .naples: print("Naples!") case .theNewYork: print("New York!") } Switch must be exhaustive. _
  72. enum class City { NEW_YORK, CHICAGO, NAPLES } let city

    = City.chicago switch city { case .chicago: print("Chicago!") case .naples: print("Naples!") case .theNewYork: print("New York!") default: fatalError("This can't be reached") }
  73. None
  74. enum City: RawRepresentable {a … }b

  75. enum City: RawRepresentable {a case chicago case newyork case naples

    init(rawValue: ESKIOFramework.City) { switch (rawValue){ case .theNewYork: self = .newyork case .chicago: self = .chicago case .naples: self = .naples default: fatalError("This can't be reached") } } var rawValue: ESKIOFramework.City { switch(self){ case .newyork: return .theNewYork case .chicago: return .chicago case .naples: return .naples } } }b
  76. enum City: RawRepresentable {a case chicago case newyork case naples

    init(rawValue: ESKIOFramework.City) { switch (rawValue){ case .theNewYork: self = .newyork case .chicago: self = .chicago case .naples: self = .naples default: fatalError("This can't be reached") } } var rawValue: ESKIOFramework.City { switch(self){ case .newyork: return .theNewYork case .chicago: return .chicago case .naples: return .naples } } }b
  77. enum City: RawRepresentable {a case chicago case newyork case naples

    case unknown init(rawValue: ESKIOFramework.City) { switch (rawValue){ case .theNewYork: self = .newyork case .chicago: self = .chicago case .naples: self = .naples default: self = .unknown } } var rawValue: ESKIOFramework.City { switch(self){ case .newyork: return .theNewYork case .chicago: return .chicago case .naples: return .naples case .unknown: fatalError("This should never happen!") } }
  78. enum class City { NEW_YORK, CHICAGO, NAPLES } let city

    = City.chicago switch city { case .chicago: print("Chicago!") case .naples: print("Naples!") case .theNewYork: print("New York!") default: fatalError("This can't be reached") }
  79. enum class City { NEW_YORK, CHICAGO, NAPLES } let city

    = City.chicago switch city { case .chicago: print("Chicago!") case .naples: print("Naples!") case .newyork: print("New York!") }
  80. enum class City { NEW_YORK, CHICAGO, NAPLES } let city

    = City(rawValue: ESKIOFramework.City.chicago) switch city { case .chicago: print("Chicago!") case .naples: print("Naples!") case .newyork: print("New York!") }
  81. enum class City { NEW_YORK, CHICAGO, NAPLES, ANYWHERE } sealed

    class Pizza(val city: City) { object ThinCrust : Pizza(NEW_YORK) object DeepDish : Pizza(CHICAGO) object Neapolitan : Pizza(NAPLES) class Dessert(val topping: String): Pizza(ANYWHERE) }
  82. enum City: RawRepresentable {a case chicago case newyork case naples

    init(rawValue: ESKIOFramework.City) { switch (rawValue){ case .theNewYork: self = .newyork case .chicago: self = .chicago case .naples: self = .naples default: fatalError("This can't be reached") } } var rawValue: ESKIOFramework.City { switch(self){ case .newyork: return .theNewYork case .chicago: return .chicago case .naples: return .naples } } }b
  83. enum City: RawRepresentable {a case chicago case newyork case naples

    case anywhere init(rawValue: ESKIOFramework.City) { switch (rawValue){ case .theNewYork: self = .newyork case .chicago: self = .chicago case .naples: self = .naples case .anywhere: self = .anywhere default: fatalError("This can't be reached") } } var rawValue: ESKIOFramework.City { switch(self){ case .newyork: return .theNewYork case .chicago: return .chicago case .naples: return .naples case .anywhere: return .anywhere }
  84. enum Pizza: RawRepresentable {a }b

  85. enum Pizza: RawRepresentable {a case deepdish(City) case thin(City) case neapolitan(City)

    case dessert(City, String) }b
  86. enum Pizza: RawRepresentable {a … init(rawValue: ESKIOFramework.Pizza) { switch (rawValue){

    case .DeepDish(): self = .deepdish(City(rawValue: rawValue.city)) … case is ESKIOFramework.Pizza.Dessert: let dessert = (rawValue as! ESKIOFramework.Pizza.Dessert) self = .dessert(City(rawValue: rawValue.city), dessert.topping) default: fatalError("This can't be reached") } } var rawValue: ESKIOFramework.Pizza { switch(self){ case .deepdish: return .DeepDish() … case let .dessert(_, topping): return ESKIOFramework.Pizza.Dessert(topping: topping) } } }b
  87. func printCity(city: City) { switch city { case .naples: print("Naples!")

    case .chicago: print("Chicago!") case .newyork: print("New Yawk!") case .anywhere: print("Anywhere!") } } func printPizza(pizza: Pizza) { switch pizza { case let .neapolitan(city): print("City: \(city)") case let .deepdish(city): print("City: \(city)") case let .thin(city): print("City: \(city)") case let .dessert(city, topping): print("City: \(city), Dessert: \(topping)") } }
  88. New Yawk! Chicago! Naples! Anywhere! City: newyork City: naples City:

    chicago City: anywhere, Dessert: nutella
  89. None
  90. Generics

  91. class StateHolder<T>(state: T) { val myState = state fun pullState():

    T = myState }
  92. class StateHolder<T>(state: T) { val myState = state fun pullState():

    T = myState } let holder = StateHolder(state: "yo") let state = holder.pullState() print(state)
  93. class StateHolder<T>(state: T) { val myState = state fun pullState():

    T = myState } let holder = StateHolder(state: "yo") let state = holder.pullState() print(state) Expression implicitly coerced `Any?` To `Any` _ .
  94. __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("StateHolder"))) @interface ESKIOFStateHolder : KotlinBase - (instancetype)initWithState:(id _Nullable)state __attribute__((swift_name("init(state:)")))

    __attribute__((objc_designated_initializer)); - (id _Nullable)pullState __attribute__((swift_name("pullState()"))); @property (readonly) id _Nullable myState __attribute__((swift_name("myState"))); @end;
  95. __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("StateHolder"))) @interface ESKIOFStateHolder : KotlinBase - (instancetype)initWithState:(id _Nullable)state __attribute__((swift_name("init(state:)")))

    __attribute__((objc_designated_initializer)); - (id _Nullable)pullState __attribute__((swift_name("pullState()"))); @property (readonly) id _Nullable myState __attribute__((swift_name("myState"))); @end;
  96. class StateHolder<T>(state: T) { val myState = state fun pullState():

    T = myState } let holder = StateHolder(state: "yo") let state = holder.pullState() print(state) Expression implicitly coerced `Any?` To `Any` _ .
  97. class StateHolder<T: Any>(state: T) { val myState = state fun

    pullState(): T = myState } let holder = StateHolder(state: "yo") let state = holder.pullState() print(state)
  98. __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("StateHolder"))) @interface ESKIOFStateHolder : KotlinBase - (instancetype)initWithState:(id _Nullable)state __attribute__((swift_name("init(state:)")))

    __attribute__((objc_designated_initializer)); - (id _Nullable)pullState __attribute__((swift_name("pullState()"))); @property (readonly) id _Nullable myState __attribute__((swift_name("myState"))); @end;
  99. __attribute__((objc_subclassing_restricted)) __attribute__((swift_name("StateHolder"))) @interface ESKIOFStateHolder : KotlinBase - (instancetype)initWithState:(id)state __attribute__((swift_name("init(state:)"))) __attribute__((objc_designated_initializer));

    - (id)pullState __attribute__((swift_name("pullState()"))); @property (readonly) id myState __attribute__((swift_name("myState"))); @end;
  100. class StateHolder<out T>(state: T) {…} fun copy(from: StateHolder<out Any>, to:

    StateHolder<Any>) {…} @interface ESKIOFStateHolder<__covariant T>
  101. None
  102. None
  103. value types

  104. struct Address { varaline1: String varaline2: String? varacity: String varastate:

    String varazip: String }a
  105. struct Address {…}a func test() { var me = Address(

    line1: "1455 Market St", line2: "Apt 1", city: "San Francisco", state: "CA", zip: "94103" ) var neighbor = me neighbor.line2 = "Apt 2" print("\(me ?? ""), \(neighbor ?? "")") // prints "Apt 1, Apt 2" }
  106. class Person { var firstName: String = "John" var lastName:

    String = "Doe" } func test() { var john = Person() var jane = john jane.firstName = "Jane" print("\(john.firstName), \(jane.firstName)") // oops, both "Jane" }
  107. struct Address { varaline1: String varaline2: String? varacity: String varastate:

    String varazip: String }a
  108. data class Address( val line1: String, val line2: String?, val

    city: String, val state: String, val zip: String )
  109. data class Address(…) fun test() { val me = Address(

    line1 = "1455 Market St", line2 = "Apt 1", city = "San Francisco", state = "CA", zip = "94103" ) val neighbor = me.copy(line2 = "Apt 2") print("$me, $neighbor") }
  110. struct Struct { var name: String } let a =

    Struct(name: "jrod") a.name = "" data class Struct(var name: String) val a = Struct(name = "jrod") a.name = "" Cannot assign …: 'a' is a 'let' constant _
  111. class Point( val x: Int, val y: Int )

  112. class Point( val x: Int, val y: Int ) value

    class Point( val x: Int, val y: Int ) Today Someday?
  113. header header x y header x y header x y

    header x y val points = arrayOf(Point(1, 2), …)
  114. header x x x x y y y y val

    points = arrayOf(Point(1, 2), …)
  115. None
  116. None
  117. Build performance

  118. None
  119. None
  120. None
  121. None
  122. None
  123. $ ./gradlew :SharedCode:packForXCode \
 -—profile --rerun-tasks -—no-build-cache -—console=plain

  124. $ ./gradlew :SharedCode:packForXCode \
 -—profile --rerun-tasks -—no-build-cache -—console=plain

  125. None
  126. None
  127. None
  128. http://bit.ly/34ZN5UQ

  129. https://github.com/JetBrains/kotlin/blob/master/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/ jetbrains/kotlin/gradle/tasks/Tasks.kt#L128

  130. Runtime performance

  131. fun mergesort(list: List<Int>): List<Int> { if (list.size <= 1) return

    list val middle = list.size / 2 val left = list.subList(0, middle) val right = list.subList(middle, list.size) return merge(mergesort(left), mergesort(right)) } fun merge(left: List<Int>, right: List<Int>): List<Int> { var iLeft = 0 var iRight = 0 val newList = mutableListOf<Int>() while (iLeft < left.size && iRight < right.size) { if (left[iLeft] <= right[iRight]) newList.add(left[iLeft++]) else newList.add(right[iRight++]) } while (iLeft < left.size) newList.add(left[iLeft++]) while (iRight < right.size) newList.add(right[iRight++]) return newList }
  132. None
  133. None
  134. None
  135. None
  136. None
  137. None
  138. None
  139. None
  140. None
  141. None
  142. None
  143. None
  144. https://bit.ly/32DOCyf

  145. None
  146. None
  147. None
  148. Recap

  149. • Beware of Int! (Swift -> Kotlin)

  150. • Beware of Int! (Swift -> Kotlin) • Beware of

    Method overloads with similar parameter names
  151. • Beware of Int! (Swift -> Kotlin) • Beware of

    Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries!
  152. • Beware of Int! (Swift -> Kotlin) • Beware of

    Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries! • Enums matter! But currently a sore spot in today's interop story
  153. • Beware of Int! (Swift -> Kotlin) • Beware of

    Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries! • Enums matter! But currently a sore spot in today's interop story • JetBrains Swift Codegen?!
  154. • Beware of Int! (Swift -> Kotlin) • Beware of

    Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries! • Enums matter! But currently a sore spot in today's interop story • JetBrains Swift Codegen?! • On the other hand, Generics seem to favor Obj-C more
  155. • Beware of Int! (Swift -> Kotlin) • Beware of

    Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries! • Enums matter! But currently a sore spot in today's interop story • JetBrains Swift Codegen?! • On the other hand, Generics seem to favor Obj-C more • No matter what progress is made, lack of value types is still a limiting factor
  156. • Beware of Int! (Swift -> Kotlin) • Beware of

    Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries! • Enums matter! But currently a sore spot in today's interop story • JetBrains Swift Codegen?! • On the other hand, Generics seem to favor Obj-C more • No matter what progress is made, lack of value types is still a limiting factor • Build performance could be better, looking forward to native compiler improvements in 1.4
  157. • Beware of Int! (Swift -> Kotlin) • Beware of

    Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries! • Enums matter! But currently a sore spot in today's interop story • JetBrains Swift Codegen?! • On the other hand, Generics seem to favor Obj-C more • No matter what progress is made, lack of value types is still a limiting factor • Build performance could be better, looking forward to native compiler improvements in 1.4 • Need that build cache issue fix removing @LocalState!
  158. • Beware of Int! (Swift -> Kotlin) • Beware of

    Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries! • Enums matter! But currently a sore spot in today's interop story • JetBrains Swift Codegen?! • On the other hand, Generics seem to favor Obj-C more • No matter what progress is made, lack of value types is still a limiting factor • Build performance could be better, looking forward to native compiler improvements in 1.4 • Need that build cache issue fix removing @LocalState! • Runtime performance isn't too bad for normal app cases, but heavy computation pays a heavier price
  159. • Beware of Method overloads with similar parameter names •

    Let's continue to grow the core Multiplatform suite of libraries! • Enums matter! But currently a sore spot in today's interop story • JetBrains Swift Codegen?! • On the other hand, Generics seem to favor Obj-C more • No matter what progress is made, lack of value types is still a limiting factor • Build performance could be better, looking forward to native compiler improvements in 1.4 • Need that build cache issue fix removing @LocalState! • Runtime performance isn't too bad for normal app cases, but heavy computation pays a heavier price * Future continues to look promising for Swift-ObjC-Kotlin interop. Wire support for iOS a target for 2020.
  160. @jrodbx

  161. #KotlinConf THANK YOU AND REMEMBER TO VOTE John Rodriguez @jrodbx