Share
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
Transcript
161 slides
-
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 -
} } 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 -
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 -
apk ipa jar framework aar aar jar Klib actual/expect expect/actual
SharedCode actual/expect expect/actual Gradle dependencies -
object Object class Clazz interface Interface { fun member() {}
} SharedCode/src/commonMain/kotlin/demoCommon.kt -
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)) -
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 -
@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 -
@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 -
@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 -
@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 -
fun plusplus(value: Int): Int { return value + 1 }
SharedCode/src/commonMain/kotlin/demoCommon.kt -
fun plusplus(value: Int): Int { return value + 1 }
let value = 5 DemoCommonKt.plusplus(value: value) -
let value = 5 DemoCommonKt.plusplus(value: value) fun plusplus(value: Int): Int
{ return value + 1 } Cannot convert value … 'Int' to … 'Int32' _ -
__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 -
__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 -
let value = 5 DemoCommonKt.plusplus(value: value) fun plusplus(value: Int): Int
{ return value + 1 } Cannot convert value … 'Int' to … 'Int32' _ -
let value = 5 as Int32 DemoCommonKt.plusplus(value: value) fun plusplus(value:
Int): Int { return value + 1 } Cannot convert value … 'Int' to … 'Int32' _ -
let value = 5 as Int32 DemoCommonKt.plusplus(value: value) fun plusplus(value:
Int): Int { return value + 1 } Cannot convert value … 'Int' to … 'Int32' _ -
fun plusplus(value: Int): Intx{ return valuex+x1 } let value =
5 as Int32 DemoCommonKt.plusplus(value: value) -
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) -
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) -
__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 -
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) -
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) -
__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; -
__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; -
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!") } -
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. _ -
__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; -
__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; -
__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; -
__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; -
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. _ -
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") } -
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 -
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 -
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!") } } -
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") } -
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!") } -
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!") } -
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) } -
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 -
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 } -
enum Pizza: RawRepresentable {a case deepdish(City) case thin(City) case neapolitan(City)
case dessert(City, String) }b -
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 -
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)") } } -
New Yawk! Chicago! Naples! Anywhere! City: newyork City: naples City:
chicago City: anywhere, Dessert: nutella -
class StateHolder<T>(state: T) { val myState = state fun pullState():
T = myState } let holder = StateHolder(state: "yo") let state = holder.pullState() print(state) -
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` _ . -
__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; -
__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; -
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` _ . -
class StateHolder<T: Any>(state: T) { val myState = state fun
pullState(): T = myState } let holder = StateHolder(state: "yo") let state = holder.pullState() print(state) -
__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; -
__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; -
class StateHolder<out T>(state: T) {…} fun copy(from: StateHolder<out Any>, to:
StateHolder<Any>) {…} @interface ESKIOFStateHolder<__covariant T> -
struct Address { varaline1: String varaline2: String? varacity: String varastate:
String varazip: String }a -
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" } -
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" } -
struct Address { varaline1: String varaline2: String? varacity: String varastate:
String varazip: String }a -
data class Address( val line1: String, val line2: String?, val
city: String, val state: String, val zip: String ) -
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") } -
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 _ -
class Point( val x: Int, val y: Int ) value
class Point( val x: Int, val y: Int ) Today Someday? -
header header x y header x y header x y
header x y val points = arrayOf(Point(1, 2), …) -
header x x x x y y y y val
points = arrayOf(Point(1, 2), …) -
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 } -
• Beware of Int! (Swift -> Kotlin) • Beware of
Method overloads with similar parameter names -
• Beware of Int! (Swift -> Kotlin) • Beware of
Method overloads with similar parameter names • Let's continue to grow the core Multiplatform suite of libraries! -
• 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 -
• 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?! -
• 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 -
• 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 -
• 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 -
• 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! -
• 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 -
• 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.