Marcel Voss

Quick Tip: Kotlin Scope Functions in Swift

by Marcel on June 5, 2020

When I recently reviewed a colleagues’ Android code, I stumbled upon a function call named with that allows for omitting a variable’s name within its scope (for example for accessing multiple members). As pointed out by that colleague, Kotlin actually has a bunch of nifty APIs that are being described as Scope Functions .

The Kotlin standard library contains several functions whose sole purpose is to execute a block of code within the context of an object. […] In this scope, you can access the object without its name. Such functions are called scope functions.

While we can not simply omit a variable’s name in Swift (without tweaking the compiler), we can at least use the shorthand syntax within a closure to access its members - this already makes it more pleasant than writing a variable’s name multiple times on several lines and can even simplify our lives from time to time.

So, what are these functions all about? Let’s take a look at the previously mentioned with function that is defined like this in Kotlin:

inline fun <T, R> with(receiver: T, block: T.() -> R): R

It essentially that takes a value T and a block that provides the previously passed value as an argument within the block and returns a value R from the same block which is then being forwarded by the function itself. When being used, it might look like this:

val numbers = mutableListOf("one", "two", "three")
with(numbers) {
    val firstItem = first()
    val lastItem = last()        
    println("First item: $firstItem, last item: $lastItem")
}

When looking at the function signature in Kotlin, it should become quite apparent that we can steal this bit of handiness and make use of it in Swift. Implementing it is simple and we could even simplify it further by making T an inout argument and being able to remove R completely:

func with<T>(_ object: T, using closure: (inout T) throws -> Void) rethrows -> T {
    var mutableObject = object
    try closure(&mutableObject)
    return mutableObject
}

When being used, our function will appear like this:

let item = ShoppingBagItem(name: "Cappucino", quantity: 3)

with(item) {
    print("Buying \($0.quantity)x \($0.name)")
}

In fact, our with function is quite flexible and could also be used for configuring values within its scope and returning a ready-to-use object (with a couple of changes).

This is one of the many examples for why Swift is a “bottom-up” language that allows us to extend it as we like (to a certain degree) and customize the way we interact with our code that fits our needs and preferences.