Skip to main content

Overloaded Functions

Overview

Kotlin, Objective-C, and Swift all have different levels of support for function overloading:

  • Kotlin distinguishes overloads by their parameter types.
  • Objective-C uses argument labels (parameter names in Kotlin) but does not use the parameter types.
  • Swift combines these two approaches and allows overloads to have the same parameter types if some argument labels differ.

Because of the differences between Kotlin and Objective-C, Kotlin has to expose some functions under a different name to avoid name collisions. The changed names are based on the original Kotlin name, but they have the _ suffix (or multiple suffixes if there are multiple collisions).

Kotlin adds this suffix to the end of the function identifier for functions without parameters. Functions with parameters have the suffix at the end of the last argument label.

note

The same mechanism is used for constructors and properties.

The problem with this solution is that renaming the functions is not necessary for Swift in most cases. So SKIE improves on this by removing the unnecessary underscores if possible.

Example

Consider the following two Kotlin functions:

Kotlin
fun foo(i: Int) {
}

fun foo(i: String) {
}

These functions can share the same name in Kotlin because Kotlin supports function overloading based on parameter types. Because Objective-C doesn't support function overloading based on parameter types, the Kotlin compiler renames one of these functions to avoid a name collision.

Therefore, these two functions would have to be called in this way:

Swift without SKIE
foo(i: 1)

foo(i_: "A")

Because this renaming is not necessary for Swift, SKIE can leave the original name intact, resulting in:

Swift with SKIE
foo(i: 1)

foo(i: "A")

Limitations

The overloading rules mentioned above are only a simplification of the actual rules. In some instances, functions do not cause a name collision in Kotlin, but they do in Swift. Therefore, SKIE still has to resort to renaming functions occasionally.

caution

With or without SKIE, relying on the automatic renaming of functions is highly discouraged. Instead, it's recommended to manually resolve all name collisions, if possible, by either renaming the conflicting functions in Kotlin or by using the @ObjCName annotation. This approach is not possible for code from 3rd party libraries, but we plan to implement support for changing the names even there.

The reason why it's not recommended to rely on the automatic renaming is that it can lead to unexpected breaking changes. For example, if you resolve the name collision by renaming the non-renamed function (from Swift's point of view). Then, the function originally renamed by SKIE will no longer be renamed.

A worse situation occurs when a new conflicting function is added. Even though the order in which the renaming happens has deterministic rules, it is not easy to predict. So, in such a situation, it's likely that the Swift code will still compile but call a different function than intended.

Because of the dangers of relying on automatic renaming, SKIE prints a warning when it has to rename a function. Since it's not always possible to resolve the name collisions manually, it's possible to suppress these warnings using SKIE configuration. Note that these warnings are not raised in all situations (yet) - especially when the renaming is done by the Kotlin compiler and not SKIE.

Non-exposed classes used as parameter type

Some types, for example, value classes, are not exposed to Objective-C.
Instead, the compiler replaces these types with some other supported type. In the case of value classes, it's the wrapped type. For this reason, some functions might end up with the same signature in Swift, even though they have different signatures in Kotlin.

For example:

Kotlin
value class V(val value: Int)

fun foo(v: V) {
}

fun foo(v: Int) {
}

results in the following Swift signatures:

Swift with SKIE
func foo(v: Int32)

func foo_(v: Int32)

Properties and parameterless functions

Kotlin supports properties with the same name as parameterless functions. For example:

Kotlin
fun foo(): Int = 1

val foo: Int = 1

The same is not true for Swift, so SKIE has to rename one of these declarations.

note

You wouldn't notice this issue without using SKIE because the Kotlin compiler uses a different workaround. Unfortunately, this workaround cannot be used by SKIE because it only works in the Swift/Obj-C interop, and SKIE generates Swift instead of Objective-C.

Functions and types

Both Kotlin and Swift prohibit functions and classes from having the same name in a single namespace. However, Kotlin and Swift have different kinds of namespaces. Kotlin has package-level namespaces, while Swift has module-level namespaces.

Because Kotlin is currently exported as a single module, and SKIE supports global functions, it's possible to create such a collision. And in that case, SKIE has to rename one of these declarations.

Migration and Compatibility

This feature creates source-breaking changes because it changes the names of some functions. However, resolving these breaking changes should be easy to do.

You will need to update the Swift code to use the new function names. Most of the time, this means removing the _ suffix from the function identifier or the last argument label. In some situations, you will have to resolve the still-existing name collisions (see Limitations).

The Swift compiler will tell you which functions were renamed in most cases. However, there is a theoretical possibility that the Swift compiler will not detect the renaming because two functions swapped their names. Such a situation could occur because the order in which the renaming happens is not guaranteed to be exactly the same in Kotlin and SKIE.

Running into this problem is very unlikely. However, to ensure this doesn't happen, resolve all collision warnings produced by SKIE. If some conflict cannot be resolved, look for all calls to the conflicting functions in Swift and check if they are still calling the expected function.

note

SKIE uses a different approach to renaming functions than the Kotlin compiler. Functions are always renamed by adding the _ suffix to the end of the function identifier - not the last argument label.

However, constructors cannot have a different identifier (it's always init). That means SKIE adds the _ suffix to the last argument label of the constructor.