Introducing the any keyword

Swift 5.6 introduced a few addition to the language, and in particular one will be quite important in the future and is the keyword any to mark existential types.
At the time of writing, this keyword is optional, but in future Swift versions some code will need to be refactored to avoid warnings and, eventually, errors at compile time.
All the code in this post can be found in this Swift playground

Existential types

Let’s start with defining what is an existential type.
I’m sure you are familiar with protocols in Swift, here is a simple one


protocol StringManipulating {
    func manipulateString(_ string: String) -> String
}

class UppercaseManipulator: StringManipulating {
    func manipulateString(_ string: String) -> String {
        string.uppercased()
    }
}

we have the StringManipulating protocol and the class UppercaseManipulator as a concrete implementation of that protocol.
Let’s take a look at a function that uses the protocol


func manipulate(string: String, using manipulator: StringManipulating) -> String {
    manipulator.manipulateString(string)
}

var manipulator: StringManipulating = UppercaseManipulator()

in this context, manipulator: StringManipulating is an existential type. This means that the variable manipulator can hold a value of any type conforming to the StringManipulating protocol at runtime.
There’s nothing wrong with it, but the compiler has to take this into account and as you’ll see later, it doesn’t come for free.

Dynamic dispatch

In the previous example we used a variabile of type StringManipulating with the function manipulate. Since it may hold any type conforming to the protocol, the program at runtime needs to determine the actual type and perform an indirect call. That’s dynamic dispatch, and as I mentioned before it doesn’t come for free so it is better to avoid it in your code especially if you need to perform the action multiple times or if the code is particularly important for having good performance in your app.
So what can you do?
You can use generics


func genericManipulate<S: StringManipulating>(string: String, using manipulator: S) -> String {
    manipulator.manipulateString(string)
}

the function is similar to the one we used before, but implemented with generics.
This time, the compiler will be able to optimise the code at the calling site because it will be able to know the concrete type at compile time.
If you try to call the function genericManipulate with a variable of type StringManipulating the compiler will complain with this error: Protocol ‘StringManipulating’ as a type cannot conform to the protocol itself
So you’ll have to pass a concrete type like UppercaseManipulator to the function, and the compiler will make the necessary optimisations and avoid dynamic dispatch.

You may wonder how much time you actually waste for dynamic dispatch.
In my playground shared on GitHub I put a simple example to measure the time needed for both implementation.
Try to run it and see for yourself, you’ll notice the function genericManipulate takes way less time than the other one with existential type. In my example I call both functions thousands of time so you can see the difference, of course if you only use it every once in a while you won’t be able to notice the difference but image running something very often in your code, going for the static dispatch may save time and make your app feel snappier.

The any keyword

One of the addition of Swift 5.6 is the any keyword, described in proposal SE 0335.
You’ll use any to explicitly mark a type as existential. Omitting the any keyword will cause the compiler to issue a warning an eventually an error, so you’ll need to refactor some of your code sooner or later.
The function will look like this


func manipulate(string: String, using manipulator: any StringManipulating) -> String {
    manipulator.manipulateString(string)
}
</s:>

and that’s it, you’ll have to be explicit about existential types with any or you’ll need to use generics to avoid the warning and avoid dynamic dispatch when is not necessary.

Happy coding 🙂