Kotlin Multiplatform / Extensions in Kotlin and Swift: Similarities and Differences

Extensions in Kotlin and Swift: Similarities and Differences

Even though extensions in Kotlin and Swift are conceptually the same in the sense that they both extend a class, they have quite a few differences in what you can do with them. Let’s start by taking a look at the basic syntax for creating an extension.

Kotlin

class MyClass { }
 fun MyClass.extensionFunction() { }

Swift

class MyClass { }
 extension MyClass {
 	func extensionFunction() { }
 }

Differences between Kotlin and Swift extensions

The table below lists key differences between extensions in these two languages.

Kotlin Swift
Adds a static function without modifying the class Modifies the class directly
Supports extensions for optional (nullable) types Extensions for optional types are not supported (but technically possible)
Possible to create extensions for generic types Not possible (workaround with NSObject)
Possible to extend a class within another class with access to fields of both classes Not possible to create an extension within a class
No access to private fields Extensions can access private fields of a class
Not possible to define a new constructor as an extension Possible to create a constructor as an extension
Not possible to implement an interface as an extension Possible to implement a protocol as an extension
Not possible to override an extension of a superclass Possible to override an extension of a superclass
Possible to add a property as an extension Possible to add a property as an extension (yes, this is not a difference)

Now let’s examine each item in detail.

How extensions work

In Kotlin extensions do not actually modify a class they extend. By declaring an extension you create a new static function, not a new class member. Which means compiling it will give you something like this:

fun extensionFunction(receiver: MyClass) { }

This difference in extension behavior is the reason for most of the differences listed below.

Extensions for optional (nullable) types

Kotlin provides a simple, clean syntax for this:

class MyClass {
    val localVal = "aaa"
 	fun doSomething() {
   	  println("Normal doSomething")
 	}
 }

 fun MyClass?.doSomething() {
 	if (this == null) println("Null doSomething")
 	else doSomething()
 }

If we try to write something along the same lines in Swift:

class MyClass {
 	let localVal = "aaa"
 	func doSomething() {
    	print("Normal doSomething")
 	}
 }

 extension MyClass? {
 	func extensionFunc() {
    	self == nil ? println("Null doSomething") : doSomething()
 	}
 }

We will get an expectable error: constrained extension must be declared on the unspecialized generic type 'Optional' with constraints specified by a 'where' clause. This gives us a hint about how we can actually implement an extension for an optional type in Swift. We need to create an extension for the enumeration Optional and declare our class as Wrapped. For example:

extension Optional where Wrapped: MyClass  {
 	func extensionFunc() {
     	self == nil ? print("Null doSomething") : self?.doSomething()
    }
 }

This shows that it is actually possible to create extensions for optional types in Swift, though in a somewhat convoluted and counter-intuitive way.

Extensions for generics

Kotlin supports the creation of extensions for generics, which means that the function will be available to every class. On top of that, generics can be used as both parameters and return values, which can look like this:

fun <T> T.parameterOrSelf(parameter: T?): T {
 	return parameter ?: this
 }

In Swift, it’s impossible to create anything similar to that. However, you can extend NSObject, which will cover only the classes inherited from NSObject and does not allow using the class, for which you invoke the function, as a parameter or a return value.

Extensions within another class

Kotlin allows creating an extension function for class A right within class B, which will allow this function to use properties of both A and B. This is done as follows:

class MyClass {
 	val myClassLocalVariable = "myClassLocalVariable"
 	fun doSomething() {
     	println("Normal doSomething")
 	}
 }

 class WrapperClass {

 	private val wrapperLocalVariable = "wrapperLocalVariable"

 	fun MyClass.printBoth() {
     	println("$wrapperLocalVariable $myClassLocalVariable")
 	}
 }

If we try to do the same in Swift:

class WrapperClass {
 	extension MyClass {
    	func insideWrapped() {
        	print("insideWrapped")
    	}
 	}
 }

we will get an error telling us that extensions can only be declared within the file scope:

declaration is only valid at file scope

Access to private fields

In Swift extensions can access the private fields of the class they extend. For example:

class MyClass {
 	private let localVal = "localVal"
 }

 extension MyClass {
 	func printLocalVal() {
    	print(localVal)
 	}
 }

If we write a similar construction in Kotlin, it will result in the following error:

class MyClass {
    private val localVal = "localVal"
 }

 fun MyClass.printLocalVal() {
 	println(localVal) // Cannot access 'localVal': it is private in 'MyClass'
 }

Adding a new constructor as an extension

Swift allows adding a custom constructor for an existing class as an extension. For instance, you can add a constructor for the String class:

extension String {
 	init(myClass: MyClass) {
     	self = myClass.localVal
 	}
 }

Kotlin, however, does not allow you to define new constructors of a class as extensions.

Using extensions to implement protocols/interfaces

Swift allows you to add a protocol implementation as an extension for a class. This means you can keep protocol implementations separate from the class itself as well as add a protocol implementation for an existing class even without having access to its source code.

protocol MyProtocol {
 	func sayHello()
 }

 extension String: MyProtocol {
    func sayHello() {
    	print("Hello String")
    }
 }

Overriding extensions of a superclass

Swift supports overriding extensions of a superclass directly within its subclass, though the overridable method must be marked with the attribute @objc. For example:

class A {}

 extension A {
 	@objc func extensionFunction() {
     	print("I am A")
 	}
 }

 class B: A {
 	override func extensionFunction() {
     	print("I am B")
 	}
 }

 let example: A = B()
 example.extensionFunction()

This code will print I am B.

Kotlin does not allow you to override extensions of a superclass within its subclasses, but you can create an extension for the subclass that has the exact same specification and it will be prioritized over the superclass extension. However, there is a case where this mechanism does not work in a very intuitive way. For example:

open class A {}
 class B: A() {}

 fun A.extensionFunction() {
 	println("I am A")
 }

 fun B.extensionFunction() {
 	println("I am B")
 }

 fun main() {
 	val b: B = B()
 	val a: A = b
 	b.extensionFunction()
 	a.extensionFunction()
 }

This code will print:

    I am B
 	I am A

So even though variable a is of class B, the superclass function is still invoked, as the variable type is specified as A.

Adding a new class property as an extension

In terms of adding properties to a class using extensions, Swift and Kotlin work virtually the same way. Both languages allow adding a custom property to an existing class, but it cannot store a value; that is, you can only have a getter for it. For example:

Kotlin

val <T> List<T>.lastIndex: Int
 	get() = size - 1

Swift

extension Double {
 	var km: Double { return self * 1_000.0 }
 	var m: Double { return self }
 	var cm: Double { return self / 100.0 }
 	var mm: Double { return self / 1_000.0 }
 	var ft: Double { return self / 3.28084 }
 }

Further reading

Kotlin vs Swift: The Extension — the article that served as a basis for this one.

Kotlin Extensions — official Kotlin documentation, Extensions section.

Swift Extensions — official Swift documentation, Extensions section.