What is the “any” keyword in Swift?
Published on: March 15, 2022With Swift 5.6, Apple added a new keyword to the Swift language: any
. As you'll see in this post, usage of the any
keyword looks very similar to how you use the some
keyword. They're both used in front of protocol names, and they both tell us something about how that protocol is used. Once you dig deeper into what any
means, you'll find that it's very different from some
. In fact, you might come to the conclusion that any
is somewhat of the opposite of some
. In this post, you will learn everything you need to know about the any
keyword in Swift as well as existentials, and what they are.
Let’s dive right into the any
keyword by taking a look at its intended use in a very simple example:
protocol Networking {
func fetchPosts() async throws -> [Post]
// ...
}
struct PostsDataSource {
let networking: any Networking
// ...
}
💡Tip: If you’re not familiar with Swift’s
some
keyword or need a refresher, check out this post on Swift’ssome
keyword.
While the any
keyword might look similar to the some
keyword in the sense that both are used in front of a protocol, and sound like they convey a message similar to “I don’t care what’s used for this type as long as it conforms to this protocol”, they’re really not the same at all. To understand their differences, we need to take a look at what existentials are in Swift.
Understanding what an existential is in Swift
While some
allows us to write code that more or less ignores, or discards, a protocol’s associated type and/or Self
requirement while expecting that every returned object in a function that returns some Protocol
has the same concrete type, the any
keyword simply annotates that a given type is a so-called existential. While you might not know what an existential is, you've probably seen them used. For example, if we look at the "old" way of writing the PostsDataSource
struct that you just saw, it would look as follows:
struct PostsDataSource {
let networking: Networking
// ...
}
Note that all I did is remove the any
keyword. The Networking
object that we use is an existential. This means that let networking
is an object that conforms to Networking
. The compiler doesn't know which object it will be, or what that object's type is. All the compiler knows is that there will be an object, any object, that will be assigned to let networking
when we initialize PostsDataSource
, and that object conforms to Networking
. We're essentially only sure that we'll have a box that contains a Networking
object. To know exactly which object was put in that box, we need to open that box at runtime, peek inside, and find the object.
It's important to know that existentials are relatively expensive to use because the compiler and runtime can’t pre-determine how much memory should be allocated for the concrete object that will fill in the existential. Whenever you call a method on an existential, like the networking
property in the snippet you saw earlier, the runtime will have to dynamically dispatch this call to the concrete object which is slower than a static dispatch that goes directly to a concrete type.
The Swift team has determined that it’s currently too easy to reach for an existential over a concrete object. This essentially means that a lot of us are writing code that uses protocols (existentials) that harm our performance without us really being aware of it. For example, there’s nothing wrong with the old fashioned PostsDataSource
you saw earlier, right?
struct PostsDataSource {
let networking: Networking
// ...
}
I’m sure we all have code like this, and in fact, we might even consider this best practice because we're not depending on concrete types which makes our code easier to test and maintain.
Sadly, this code uses an existential by having a property that has Networking
as its type. This means that it’s not clear for the runtime how much memory should be allocated for the object that will fill in our networking
property, and any calls to fetchPosts
will need to be dynamically dispatched.
By introducing the any
keyword, the language forces us to think about this. In Swift 5.6 annotating our let networking: Networking
with any
is optional; we can do this on our own terms. However, in Swift 6 it will be required to annotate existentials with the any
keyword.
Verify your existential usage for Swift 6 with Xcode 15.3
If you want to make sure that your app is ready for Swift 6.0 and uses any
or some
everywhere you're supposed to, pass the -enable-upcoming-feature ExistentialAny
in your Swift build flags. To learn how, take a look at this post where I dig into experimental Swift versions and features. Note that the EsistentialAny
build flag is available in the default Xcode 15.3 toolchain.
Digging deeper into the any keyword
As I was reading the proposal for any
, I realized that what the Swift team seems to want us to do, is to use generics and concrete types rather than existentials when possible. It’s especially this part from the introduction of the proposal that made this clear to me:
Despite these significant and often undesirable implications, existential types have a minimal spelling. Syntactically, the cost of using one is hidden, and the similar spelling to generic constraints has caused many programmers to confuse existential types with generics. In reality, the need for the dynamism they provided is relatively rare compared to the need for generics, but the language makes existential types too easy to reach for, especially by mistake. The cost of using existential types should not be hidden, and programmers should explicitly opt into these semantics.
So how should we be writing our PostsDataSource
without depending on a concrete implementation directly? And how can we do that without using an existential since clearly existentials are less than ideal?
The easiest way would be to add a generic to our PostsDataSource
and constraining it to Networking
as follows:
protocol Networking {
func fetchPosts() async throws -> [Post]
// ...
}
struct PostsDataSource<Network: Networking> {
let networking: Network
// ...
}
By writing our code like this, the compiler will know up front which type will be used to fill in the Network
generic. This means that the runtime will know up-front how much memory needs to be allocated for this object, and calls to fetchPosts
can be dispatched statically rather than dynamically.
💡Tip: If you’re not too familiar with generics, take a look at this article to learn more about generics in Swift and how they’re used.
When writing PostsDataSource
as shown above, you don’t lose anything valuable. You can still inject different concrete implementations for testing, and you can still have different instances of PostsDataSource
with different networking objects even within your app. The difference compared to the previous approach is that the runtime can more efficiently execute your code when it know the concrete types you’re using (through generics).
Alternatively, you could rewrite let networking
to use some Networking
instead of using a generic. To learn more about some
, and how you can use it to replace generics in some situations, take a look at this post.
The only thing we’ve lost by not using any
is the ability to dynamically swap out the networking implementation at runtime by assigning a new value of a different concrete type to networking
(which we couldn’t do anyway because it’s defined as a let
).
It's interesting to note that because we have to choose between any
, some
, and a generic, when we define our let networking
, it's easier to choose the correct option. We could use : any Networking
wherever we'd write : Networking
in Swift 5.5 and earlier, and our code would work just fine but we might be using a suboptimal existential instead of a concrete type that can benefit from compile-time optimizations and static dispatch at runtime. In some cases, that's exactly what you want. You might need the flexibility that an existential provides, but often you'll find that you don't need an existential at all.
So how useful is the any
keyword really? Should you be using it in Swift 5.6 already or is it better to just wait until the compiler starts enforcing any in Swift 6?
In my opinion, the any
keyword will provide developers with an interesting tool that forces them to think about how they write code, and more specifically, how we use types in our code. Given that existentials have a detrimental effect on our code’s performance I’m happy to see that we need to explicitly annotate existentials with a keyword in Swift 6 onward. Especially because it’s often possible to use a generic instead of an existential without losing any benefits of using protocols. For that reason alone it’s already worth training yourself to start using any
in Swift 5.6.
Note: take a look at my post comparing some and any to learn a bit more about how
some
can be used in place of a generic in certain situations.
Using any
now in Swift 5.6 will smoothen your inevitable transition to Swift 6 where the following code would actually be a compiler error:
protocol Networking {
func fetchPosts() async throws -> [Post]
// ...
}
struct PostsDataSource {
// This is an error in Swift 6 because Networking is an existential
let networking: Networking
// ...
}
The above code will at least need to be written using any Networking
in Swift if you really need the existential Networking
. In most cases however, this should prompt you to reconsider using the protocol in favor of a generic or writing some Networking
in order to improve runtime performance.
Whether or not the performance gains from using generics over existentials is significant enough to make a difference in the average app remains to be seen. Being conscious of the cost of existentials in Swift is good though, and it’s definitely making me reconsider some of the code I have written.
The any keyword in Swift 5.7
In Swift 5.7 the any
keyword is still not mandatory for all existentials but certain features aren't available to non-any
protocols. For example, in Swift 5.7 the requirements around protocols with a Self
requirement have been relaxed. Previously, if you wanted to use a protocol with an associated type of Self
requirement as a type you would have to use some
. This is why you have to write var body: some View
in SwiftUI.
In Swift 5.7 this restriction is relaxed, but you have to write any
to use an existential that has an associated type or Self
requirement. The following example is an example of this:
protocol Content: Identifiable {
var url: URL { get }
}
func useContent(_ content: any Content) {
// ...
}
The code above requires us to use any Content
because Content
extends the Identifiable
protocol which has an associated type (defined as associatedtype ID: Hashable
). For that reason, we have to use any
if we can't use some
.
The same is true for protocols that use a primary associated type. Using an existential with a primary associated type already requires the any
keyword in Swift 5.7.
Note that any
isn't a drop in replacement for some
as noted in my comparison of these two keywords. When using any
, you'll always opt-in to using an existential rather than a concrete type (which is what some
would provide).
Even though any
won't be completely mandatory until Swift 6.0 it's interesting to see that Swift 5.7 already requires any
for some of the new features that were made available with Swift 5.7. I think this reinforces the point that I made earlier in this post; try to start using any
today so you're not surprised by compiler errors once Swift 6.0 drops.