Bridging Reference
Skipβs documentation describes Skipβs technology for bridging between compiled Swift and transpiled Swift or Kotlin and Java. This reference details the Swift language features and types that can be bridged. Bridging capabilities are symmetrical unless otherwise noted. That is, if something is marked as bridgeable, then you can use it whether you are bridging from native Swift to Kotlin/Java, or from transpiled Kotlin/Java to native Swift.
Bridging support will expand in future releases. Most current limitations are due to lack of time and testing rather than technical hurdles.
Language Features
The following table details Skipβs support for bridging various Swift language features. A β indicates that a feature is fully or very strongly supported. A ~ indicates that a feature is partially supported. And a β indicates that a feature is not supported, or is only weakly supported. Future releases may address some unsupported language features, but others reflect deep incompatibilities between the Swift and Kotlin languages and runtimes.
- β Classes
- β Inheritance - up to 4 levels
- β Structs - see the Mutable Structs topic below
- β Constructor synthesis
- β
Equatable
synthesis - β
Hashable
synthesis
- β Protocols
- β Inheritance
- β Property requirements
- β Function requirements
- β Constructor requirements
- β Static requirements
- β Enums
- β Enums without associated values
- β Enums with associated values
- β Mutating properties and functions
- β Nested types
- ~ Extensions
- β Concrete type extensions
- ~ Protocol extensions
- β Extending a type within the current module
- β Extending a type in another module
- ~ Generic types - see the Generics topic below
- β Tuples - up to 5 elements
- β Not supported as collection elements or closure parameters
- β Typealiases
- β Properties
- β Globals
- β Members
- β
let
- β
var
- β Static properties
- β Stored properties
- β Computed properties
- β Throwing properties
- β Lazy properties
- β Functions
- β Globals
- β Members
- β Overloading on types
- β Overloading on param labels
- β Overloading on return type
- β Static functions
- ~ Generic functions - see the Generics topic below
- β Throwing functions
- β Default parameter values
- β
inout
parameters - β Variadic parameters
- β
@autoclosure
parameters - β Parameter packs
- β Constructors
- β Optional constructors
- β Deconstructors
- β Closures - up to 5 parameters
- β Not supported as collection elements or parameters to other closures
- β Errors - see the Errors topic below
- ~ Concurrency
- β Async functions
- β Async properties
- β Async closures
- β
@MainActor
(Native Swift does not yet integrate with the Android run loop) - β Custom actors
- Non-private mutable properties not supported. Use functions to mutate state
- ~ Operators
- β Custom
Equatable
with==
- β Custom
Hashable
withhash(into:)
- β Custom
Comparable
with<
- β Custom subscript operators
- β
callAsFunction
support - β Other custom operators
- β Custom
- β Key paths
Builtin Types
The following table details Skipβs support for bridging builtin Swift standard library types.
- β
Any
- β
AnyHashable
- β
AnyObject
- β
Bool
- β
Character
- β Numeric types
- ~
Int
is 32 bit on JVM - β Unsigned types
- ~
- β
String
- β Optionals
- β Compound types (e.g.
A & B
) - β Fully-qualified Kotlin/Java types - translate to
AnyDynamicObject
- β
Array
- translates tokotlin.collections.List
inkotlincompat
mode - β
Data
- translates to[byte]
inkotlincompat
mode - β
Date
- translates tojava.util.Date
inkotlincompat
mode - β
Dictionary
- translates tokotlin.collections.Map
inkotlincompat
mode - β
Error
- β
OptionSet
- β
Result
- translates tokotlin.Pair<Success?, Failure?>
inkotlincompat
mode - β
Set
- translates tokotlin.collections.Set
inkotlincompat
mode - β
2-Tuple
- translates tokotlin.Pair
inkotlincompat
mode - β
3-Tuple
- translates tokotlin.Triple
inkotlincompat
mode - β
URL
- translates tojava.net.URI
inkotlincompat
mode - β
UUID
- translates tojava.util.UUID
inkotlincompat
mode
Special Topics
Equality
Do not rely on object identity and ===
comparisons of bridged instances. The same object may get wrapped by multiple bridging instances. Because Kotlin/Java types have built-in equals
and hashCode
functions that default to using identity, Skipβs Kotlin projections of your native types will implement equals
and hashCode
so that wrappers around the same native instance will compare equal and have the same hash.
Skip supports bridging of Equatable
, Hashable
, and Comparable
types, so you should implement these protocols for any additional needs.
Errors
Skip supports bridging your custom Error
types as well as functions that may throw errors. Keep in mind the following:
Error
types will extendException
when translated to Kotlin, so your bridged SwiftError
types cannot be subclasses.- You can bridge functions that throw
Error
types that are not themselves bridged. You must treat these throwing functions as if they might throw any error at all: use generalcatch
blocks that do not rely on catching a specific type of error.
Generics
Kotlin and Swift have very different generic implementation strategies. Swift generics are built deep into the language as first-class citizens of its type system. Kotlin generics, on the other hand, donβt exist at the JVM level and are only present at compile time. Additionally, the Java Native Interface (JNI) API that allows Swift and Kotlin to communicate is based on the C language, which has no generics at all. This makes it impossible to preserve generic information across the JNI boundary.
All of these factors conspire to limit the bridging of generic functions and types. Whether bridging from compiled Swift to Kotlin or vice versa, the following restrictions apply:
- You cannot bridge a subclass of a generic type.
- Static members of generic types are limited. Skip can only support static members that either donβt use the defining typeβs generics or that can be converted into a generic function that is defined independently of the defining typeβs generics.
- Generic specializations by type extensions (e.g.
extension C where T: Equatable
) cannot be bridged. - Inner types on generic outer types are not supported.
- Kotlin does not allow constructor functions to use generics other than those of the defining type.
- Kotlin does not allow
typealiases
to include generic constraints (e.g.where T: Equatable
).
Bridging from transpiled Kotlin to compiled Swift has this additional limitation:
- If a transpiled Kotlin generic type is returned from a property or function whose exact type is unknown (i.e. is of type
Any
or is a protocol type, etc), the exact typing is lost in the transfer to Swift. For example if an instance ofC<T>
is read from a bridged KotlinAny
property, the Swift side will receive an instance ofC<Any>
, regardless ofT
βs type on the Kotlin side. Similarly,C<T: P>
will bridge from anAny
field asC<P>
.
This does not apply when bridging the other direction, from compiled Swift to Kotlin. That direction, however, has the following restrictions instead:
- You cannot construct instances of a bridged Swift generic type from Kotlin. So your Swift code can create and expose generic types to Kotlin, but your Kotlin code has no way to construct Swift generic instances. You must comment any public constructors on Swift generic types with
// SKIP @nobridge
so that they are not bridged to Kotlin. - Kotlin also cannot access static members of Swift generic types. You must comment public static members with
// SKIP @nobridge
as well. - Global generic functions do not retain generic type information when called from Kotlin. So when called from Kotlin, a global Swift function
f<T>(p: T)
will always be invoked as ifT
is of typeAny
. Similarly,f<T: P>(p: P)
will always be invoked as ifT
is of typeP
.
Mutable Structs
We only recommend bridging native mutable structs if their projection will be consumed by transpiled Swift, where Skip can maintain value semantics on the Kotlin side. If you plan on using your native types from pure Kotlin or Java code, stick to classes, which mirror Kotlin/Javaβs reference semantics.
We also recommend against bridging transpiled mutable structs to native Swift, as they require a significant amount of object copying on the JVM side.