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

John Rodriguez

December 06, 2019
Tweet

More Decks by John Rodriguez

Other Decks in Technology

Transcript

  1. 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
  2. } } 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
  3. 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
  4. apk ipa jar framework aar aar jar Klib actual/expect expect/actual

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

    } SharedCode/src/commonMain/kotlin/demoCommon.kt
  6. 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))
  7. 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
  8. @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
  9. @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
  10. @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
  11. @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
  12. fun plusplus(value: Int): Int { return value + 1 }

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

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

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

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

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

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

    5 as Int32 DemoCommonKt.plusplus(value: value)
  19. 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)
  20. 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)
  21. __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
  22. 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)
  23. 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)
  24. __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;
  25. __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;
  26. 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!") }
  27. 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. _
  28. __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;
  29. __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;
  30. __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;
  31. __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;
  32. 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. _
  33. 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") }
  34. 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
  35. 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
  36. 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!") } }
  37. 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") }
  38. 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!") }
  39. 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!") }
  40. 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) }
  41. 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
  42. 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 }
  43. 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
  44. 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)") } }
  45. class StateHolder<T>(state: T) { val myState = state fun pullState():

    T = myState } let holder = StateHolder(state: "yo") let state = holder.pullState() print(state)
  46. 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` _ .
  47. __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;
  48. __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;
  49. 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` _ .
  50. class StateHolder<T: Any>(state: T) { val myState = state fun

    pullState(): T = myState } let holder = StateHolder(state: "yo") let state = holder.pullState() print(state)
  51. __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;
  52. class StateHolder<out T>(state: T) {…} fun copy(from: StateHolder<out Any>, to:

    StateHolder<Any>) {…} @interface ESKIOFStateHolder<__covariant T>
  53. 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" }
  54. 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" }
  55. data class Address( val line1: String, val line2: String?, val

    city: String, val state: String, val zip: String )
  56. 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") }
  57. 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 _
  58. class Point( val x: Int, val y: Int ) value

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

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

    points = arrayOf(Point(1, 2), …)
  61. 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 }
  62. • Beware of Int! (Swift -> Kotlin) • Beware of

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

    Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries!
  64. • 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
  65. • 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?!
  66. • 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
  67. • 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
  68. • 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
  69. • 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!
  70. • 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
  71. • 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.