This commit is contained in:
Watson David
2018-09-14 16:06:11 +02:00
committed by Nikolay Igotti
parent 5b63cdb4b2
commit a426738213
+45 -46
View File
@@ -1,6 +1,6 @@
# _Kotlin/Native_ interoperability with Swift/Objective-C # _Kotlin/Native_ interoperability with Swift/Objective-C
This documents covers some details of Kotlin/Native interoperability with This document covers some details of Kotlin/Native interoperability with
Swift/Objective-C. Swift/Objective-C.
## Usage ## Usage
@@ -10,12 +10,12 @@ Objective-C frameworks and libraries can be used in Kotlin code if
properly imported to the build (system frameworks are imported by default). properly imported to the build (system frameworks are imported by default).
See e.g. "Interop libraries" in See e.g. "Interop libraries" in
[Gradle plugin documentation](GRADLE_PLUGIN.md#building-artifacts). [Gradle plugin documentation](GRADLE_PLUGIN.md#building-artifacts).
Swift library can be used in Kotlin code if its API is exported to Objective-C A Swift library can be used in Kotlin code if its API is exported to Objective-C
with `@objc`. Pure Swift modules are not yet supported. with `@objc`. Pure Swift modules are not yet supported.
Kotlin module can be used in Swift/Objective-C code if compiled into a Kotlin modules can be used in Swift/Objective-C code if compiled into a
[framework](GRADLE_PLUGIN.md#framework). See [calculator sample](https://github.com/JetBrains/kotlin-native/tree/master/samples/calculator) [framework](GRADLE_PLUGIN.md#framework). See [calculator sample](https://github.com/JetBrains/kotlin-native/tree/master/samples/calculator)
as an example. for an example.
## Mappings ## Mappings
@@ -50,17 +50,17 @@ The table below shows how Kotlin concepts are mapped to Swift/Objective-C and vi
Objective-C classes are imported into Kotlin with their original names. Objective-C classes are imported into Kotlin with their original names.
Protocols are imported as interfaces with `Protocol` name suffix, Protocols are imported as interfaces with `Protocol` name suffix,
i.e. `@protocol Foo` -> `interface FooProtocol`. i.e. `@protocol Foo` -> `interface FooProtocol`.
These classes and interfaces are placed into package [specified in build configuration](#usage) These classes and interfaces are placed into a package [specified in build configuration](#usage)
(`platform.*` packages for preconfigured system frameworks). (`platform.*` packages for preconfigured system frameworks).
Names of Kotlin classes and interfaces are prefixed when imported to Swift/Objective-C. The names of Kotlin classes and interfaces are prefixed when imported to Swift/Objective-C.
The prefix is derived from the framework name. The prefix is derived from the framework name.
### Initializers ### Initializers
Swift/Objective-C initializers are imported to Kotlin as constructors and factory methods Swift/Objective-C initializers are imported to Kotlin as constructors and factory methods
named `create`. The latter happens with initializers declared in Objective-C category or named `create`. The latter happens with initializers declared in the Objective-C category or
as Swift extension, because Kotlin has no concept of extension constructors. as a Swift extension, because Kotlin has no concept of extension constructors.
Kotlin constructors are imported as initializers to Swift/Objective-C. Kotlin constructors are imported as initializers to Swift/Objective-C.
@@ -93,11 +93,10 @@ Framework.foo()
Generally Swift argument labels and Objective-C selector pieces are mapped to Kotlin Generally Swift argument labels and Objective-C selector pieces are mapped to Kotlin
parameter names. Anyway these two concepts have different semantics, so sometimes parameter names. Anyway these two concepts have different semantics, so sometimes
Swift/Objective-C methods can be imported with clashing Kotlin signature. In this case Swift/Objective-C methods can be imported with a clashing Kotlin signature. In this case
clashing methods can be called from Kotlin using named arguments, e.g.: the clashing methods can be called from Kotlin using named arguments, e.g.:
<div class="sample" markdown="1" theme="idea" mode="swift"> <div class="sample" markdown="1" theme="idea" mode="swift">
```swift ```swift
[player moveTo:LEFT byMeters:17] [player moveTo:LEFT byMeters:17]
[player moveTo:UP byInches:42] [player moveTo:UP byInches:42]
@@ -105,7 +104,7 @@ clashing methods can be called from Kotlin using named arguments, e.g.:
</div> </div>
in Kotlin would be: in Kotlin it would be:
<div class="sample" markdown="1" theme="idea" data-highlight-only> <div class="sample" markdown="1" theme="idea" data-highlight-only>
@@ -119,13 +118,13 @@ player.moveTo(UP, byInches = 42)
### Errors and exceptions ### Errors and exceptions
Kotlin has no concept of checked exceptions, all Kotlin exceptions are unchecked. Kotlin has no concept of checked exceptions, all Kotlin exceptions are unchecked.
Swift has only checked errors. So if Swift or Objective-C code calls Kotlin method Swift has only checked errors. So if Swift or Objective-C code calls a Kotlin method
which throws an exception to be handled, then Kotlin method should be marked which throws an exception to be handled, then the Kotlin method should be marked
with `@Throws` annotation. In this case all Kotlin exceptions with a `@Throws` annotation. In this case all Kotlin exceptions
(except for instances of `Error`, `RuntimeException` and subclasses) are translated to (except for instances of `Error`, `RuntimeException` and subclasses) are translated into
Swift error/`NSError`. a Swift error/`NSError`.
Note that the opposite translation is not implemented yet: Note that the opposite reversed translation is not implemented yet:
Swift/Objective-C error-throwing methods aren't imported to Kotlin as Swift/Objective-C error-throwing methods aren't imported to Kotlin as
exception-throwing. exception-throwing.
@@ -133,12 +132,12 @@ exception-throwing.
Members of Objective-C categories and Swift extensions are imported to Kotlin Members of Objective-C categories and Swift extensions are imported to Kotlin
as extensions. That's why these declarations can't be overridden in Kotlin. as extensions. That's why these declarations can't be overridden in Kotlin.
And extension initializers aren't available as Kotlin constructors. And the extension initializers aren't available as Kotlin constructors.
### Kotlin singletons ### Kotlin singletons
Kotlin singleton (made with `object` declaration, including `companion object`) Kotlin singleton (made with an `object` declaration, including `companion object`)
is imported to Swift/Objective-C as class with a single instance. is imported to Swift/Objective-C as a class with a single instance.
The instance is available through the factory method, i.e. as The instance is available through the factory method, i.e. as
`[MySingleton mySingleton]` in Objective-C and `MySingleton()` in Swift. `[MySingleton mySingleton]` in Objective-C and `MySingleton()` in Swift.
@@ -146,10 +145,10 @@ The instance is available through the factory method, i.e. as
While Kotlin primitive types in some cases are mapped to `NSNumber` While Kotlin primitive types in some cases are mapped to `NSNumber`
(e.g. when they are boxed), `NSNumber` type is not automatically translated (e.g. when they are boxed), `NSNumber` type is not automatically translated
to Kotlin primitive types when used as Swift/Objective-C parameter type or return value. to Kotlin primitive types when used as a Swift/Objective-C parameter type or return value.
The reason is that `NSNumber` type doesn't provide enough information The reason is that `NSNumber` type doesn't provide enough information
about wrapped primitive value type, i.e. `NSNumber` is statically not known about a wrapped primitive value type, i.e. `NSNumber` is statically not known
to be e.g. `Byte`, `Boolean` or `Double`. So Kotlin primitive values to be a e.g. `Byte`, `Boolean`, or `Double`. So Kotlin primitive values
should be cast to/from `NSNumber` manually (see [below](#casting-between-mapped-types)). should be cast to/from `NSNumber` manually (see [below](#casting-between-mapped-types)).
### NSMutableString ### NSMutableString
@@ -160,11 +159,11 @@ All instances of `NSMutableString` are copied when passed to Kotlin.
### Collections ### Collections
Kotlin collections are converted to Swift/Objective-C collections as described Kotlin collections are converted to Swift/Objective-C collections as described
by the table above. Swift/Objective-C collections are mapped to Kotlin in the same way, in the table above. Swift/Objective-C collections are mapped to Kotlin in the same way,
except for `NSMutableSet` and `NSMutableDictionary`. `NSMutableSet` isn't converted to except for `NSMutableSet` and `NSMutableDictionary`. `NSMutableSet` isn't converted to
Kotlin `MutableSet`. To pass an object for Kotlin `MutableSet`, a Kotlin `MutableSet`. To pass an object for Kotlin `MutableSet`,
one can create this kind of Kotlin collection explicitly by either creating it you can create this kind of Kotlin collection explicitly by either creating it
in Kotlin with e.g. `mutableSetOf()`, or using `${prefix}MutableSet` class in in Kotlin with e.g. `mutableSetOf()`, or using the `${prefix}MutableSet` class in
Swift/Objective-C, where `prefix` is the framework names prefix. Swift/Objective-C, where `prefix` is the framework names prefix.
The same holds for `MutableMap`. The same holds for `MutableMap`.
@@ -175,8 +174,8 @@ Swift functions / Objective-C blocks. However there is a difference in how
types of parameters and return values are mapped when translating a function types of parameters and return values are mapped when translating a function
and a function type. In the latter case primitive types are mapped to their and a function type. In the latter case primitive types are mapped to their
boxed representation, `NSNumber`. Kotlin `Unit` return value is represented boxed representation, `NSNumber`. Kotlin `Unit` return value is represented
as corresponding `Unit` singleton in Swift/Objective-C. The value of this singleton as a corresponding `Unit` singleton in Swift/Objective-C. The value of this singleton
can be retrieved in the same way as for any other Kotlin `object` can be retrieved in the same way as it is for any other Kotlin `object`
(see singletons in the table above). (see singletons in the table above).
To sum the things up: To sum the things up:
@@ -213,8 +212,8 @@ foo {
## Casting between mapped types ## Casting between mapped types
When writing Kotlin code, an object may require to be converted from Kotlin type When writing Kotlin code, an object may need to be converted from a Kotlin type
to equivalent Swift/Objective-C type (or vice versa). In this case plain old to the equivalent Swift/Objective-C type (or vice versa). In this case a plain old
Kotlin cast can be used, e.g. Kotlin cast can be used, e.g.
<div class="sample" markdown="1" theme="idea" data-highlight-only> <div class="sample" markdown="1" theme="idea" data-highlight-only>
@@ -233,22 +232,22 @@ val nsNumber = 42 as NSNumber
Kotlin classes and interfaces can be subclassed by Swift/Objective-C classes Kotlin classes and interfaces can be subclassed by Swift/Objective-C classes
and protocols. and protocols.
Currently a class that adopts Kotlin protocol should inherit `NSObject` Currently a class that adopts the Kotlin protocol should inherit `NSObject`
(either directly or indirectly). Note that all Kotlin classes do inherit `NSObject`, (either directly or indirectly). Note that all Kotlin classes do inherit `NSObject`,
so a Swift/Objective-C subclass of Kotlin class can adopt Kotlin protocol. so a Swift/Objective-C subclass of Kotlin class can adopt the Kotlin protocol.
### Subclassing Swift/Objective-C classes and protocols from Kotlin ### Subclassing Swift/Objective-C classes and protocols from Kotlin
Swift/Objective-C classes and protocols can be subclassed with Kotlin `final` class. Swift/Objective-C classes and protocols can be subclassed with a Kotlin `final` class.
Non-`final` Kotlin classes inherting Swift/Objective-C types aren't supported yet, so it is Non-`final` Kotlin classes inheriting Swift/Objective-C types aren't supported yet, so it is
not possible to declare a complex class hierarchy inherting Swift/Objective-C types. not possible to declare a complex class hierarchy inheriting Swift/Objective-C types.
Normal methods can be overridden using `override` Kotlin keyword. In this case Normal methods can be overridden using the `override` Kotlin keyword. In this case
overriding method must have the same parameter names as the overridden one. the overriding method must have the same parameter names as the overridden one.
Sometimes it is required to override initializers, e.g. when subclassing `UIViewController`. Sometimes it is required to override initializers, e.g. when subclassing `UIViewController`.
Initializers imported as Kotlin constructors can be overridden by Kotlin constructors Initializers imported as Kotlin constructors can be overridden by Kotlin constructors
marked with `@OverrideInit` annotation: marked with the `@OverrideInit` annotation:
<div class="sample" markdown="1" theme="idea" mode="swift"> <div class="sample" markdown="1" theme="idea" mode="swift">
@@ -264,16 +263,16 @@ class ViewController : UIViewController {
The overriding constructor must have the same parameter names and types as the overridden one. The overriding constructor must have the same parameter names and types as the overridden one.
To override different methods with clashing Kotlin signatures, one can add To override different methods with clashing Kotlin signatures, you can add a
`@Suppress("CONFLICTING_OVERLOADS")` annotation to the class. `@Suppress("CONFLICTING_OVERLOADS")` annotation to the class.
By default Kotlin/Native compiler doesn't allow to call non-designated By default the Kotlin/Native compiler doesn't allow calling a non-designated
Objective-C initializer as `super(...)` constructor. This behaviour can be Objective-C initializer as a `super(...)` constructor. This behaviour can be
inconvenient if designated initializers aren't marked properly in the Objective-C inconvenient if the designated initializers aren't marked properly in the Objective-C
library. Adding `disableDesignatedInitializerChecks = true` to `.def` file for library. Adding a `disableDesignatedInitializerChecks = true` to the `.def` file for
this library would disable these compiler checks. this library would disable these compiler checks.
## C features ## C features
See [INTEROP.md](INTEROP.md) for the case when library uses some plain C features See [INTEROP.md](INTEROP.md) for an example case where the library uses some plain C features
(e.g. unsafe pointers, structs etc.). (e.g. unsafe pointers, structs etc.).