What’s the difference between any and some in Swift?
Published on: June 8, 2022Protocols are an extremely important part in the Swift language, and in recent updates we've received some new capabilities around protocol and generics that allow us to be much more intentional about how we use protocols in our code. This is done through the any
and some
keywords.
In this post, you will learn everything you need to know about the similarities and differences between these two keywords. We'll start with an introduction of each keyword, and then you'll learn a bit more about the problems each keyword solves, and how you can decide whether you should use some
or any
in your code.
The some
keyword
In Swift 5.1 Apple introduced the some
keyword. This keyword was key in making SwiftUI work because the View
protocol defines an associated type which means that the View
protocol couldn't be used as a type.
The following code shows how the View
protocol is defined. As you'll notice, there's an associated type Body
:
protocol View {
associatedtype Body: View
@ViewBuilder @MainActor var body: Self.Body { get }
}
If you’d try to write var body: View
instead of var body: some View
you’d see the following compiler error in Swift 5.7:
Use of protocol 'View' as a type must be written 'any View’
Or in older versions of Swift you’d see the following:
protocol can only be used as a generic constraint because it has Self or associated type requirements
The some
keyword fixes this by hiding the concrete associated type from whoever interacts with the object that has some Protocol
as its type. More on this later.
For a full overview of the some
keyword, please refer to this post.
The any
keyword
In Swift 5.6, the any
keyword was added to the Swift language.
While it sounds like the any
keyword acts as a type erasing helper, all it really does is inform the compiler that you opt-in to using an existential (a box type that conforms to a protocol) as your type.
Code that you would originally write as:
func getObject() -> SomeProtocol {
/* ... */
}
Should be written as follows in Swift 5.6 and above:
func getObject() -> any SomeProtocol {
/* ... */
}
This makes it explicit that the type you return from getObject
is an existential (a box type) rather than a concrete object that was resolved at compile time. Note that using any
is not mandatory yet, but you should start using it. Swift 6.0 will enforce any
on existentials like the one that's used in the example you just saw.
Since both any
and some
are applied to protocols, I want to put them side by side in this blog post to better explain the problems they solve, and how you should decide whether you should use any
, some
, or something else.
For a full overview of the any
keyword, please refer to this post.
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.
Understanding the problems that any and some solve
To explain the problems solved by any
we should look at a somewhat unified example that will allow us to cover both keywords in a way that makes sense. Imagine the following protocol that models a Pizza
:
protocol Pizza {
var size: Int { get }
var name: String { get }
}
It’s a simple protocol but it’s all we need. In Swift 5.6 you might have written the following function to receive a Pizza
:
func receivePizza(_ pizza: Pizza) {
print("Omnomnom, that's a nice \(pizza.name)")
}
When this function is called, the receivePizza
function receives a so-called box type for Pizza
. In order to access the pizza name, Swift has to open up that box, grab the concrete object that implements the Pizza
protocol, and then access name
. This means that there are virtually no compile time optimizations on Pizza
, making the receivePizza
method more expensive than we’d like.
Furthermore, the following function looks pretty much the same, right?
func receivePizza<T: Pizza>(_ pizza: T) {
print("Omnomnom, that's a nice \(pizza.name)")
}
There’s a major difference here though. The Pizza
protocol isn’t used as a type here. It’s used as a constraint for T
. The compiler will be able to resolve the type of T
at compile time and receivePizza
will receive a concrete instance of a type rather than a box type.
Because this difference isn’t always clear, the Swift team has introduced the any
keyword. This keyword does not add any new functionality. Instead, it forces us to clearly communicate “this is an existential”:
func receivePizza(_ pizza: any Pizza) {
print("Omnomnom, that's a nice \(pizza.name)")
}
The example that uses a generic <T: Pizza>
does not need the any
keyword because Pizza
is used as a constraint and not as an existential.
Now that we have a clearer picture regarding any
, let’s take a closer look at some
.
In Swift, many developers have tried to write code like this:
let someCollection: Collection
Only to be faced by a compiler error to tell them that Collection
has a Self
or associated type requirement. In Swift 5.1 we can write some Collection
to tell the compiler that anybody that accesses someCollection
should not concern themselves with the specifics of the associated type and/or the Self
requirement. They should just know that this thing conforms to Collection
and that’s all. There's no information about the associated type, and the information about Self
is not made available.
This mechanism is essential to making SwiftUI’s View
protocol work.
The downside of course is that anybody that works with a some Collection
, some Publisher
, or some View
can’t access any of the generic specializations. That problem is solved by primary associated types which you can read more about right here.
However, not all protocols have associated type requirements. For example, our Pizza
protocol does not have an associated type requirement but it can benefit from some
in certain cases.
Consider this receivePizza
version again:
func receivePizza<T: Pizza>(_ pizza: T) {
print("Omnomnom, that's a nice \(pizza.name)")
}
We defined a generic T
to allow the compiler to optimize for a given concrete type of Pizza
. The some
keyword also allows the compiler to know at compile time what the underlying type for the some
object will be; it just hides this from the user of the object. This is exactly what <T: Pizza>
also does. We can only access on T
what is exposed by Pizza
. This means that we can rewrite receivePizza<T: Pizza>(_:)
as follows:
func receivePizza(_ pizza: some Pizza) {
print("Omnomnom, that's a nice \(pizza.name)")
}
We don’t need T
anywhere else, so we don’t need to “create” a type to hold our pizza. We can just say “this function takes some Pizza
" instead of “this function takes some Pizza
that we’ll call T
". Small difference, but much easier to write. And functionally equivalent.
Choosing between any and some
Once you understand the use cases for any
and some
, you’ll realize that it’s not a matter of choosing one over the other. They each solve their own very similar problems and there’s always a more correct choice.
Generally speaking you should prefer using some
or generics over any
whenever you can. You often don’t want to use a box that conforms to a protocol; you want the object that conforms to the protocol.
Or sticking with our pizza analogy, any
will hand the runtime a box that says Pizza
and it will need to open the box to see which pizza is inside. With some
or generics, the runtime will know exactly which pizza it just got, and it’ll know immediately what to do with it (toss if it’s Hawaii, keep if it’s pepperoni).
In lots of cases you’ll find that you actually didn’t mean to use any
but can make some
or a generic work, and according to the Swift team, we should always prefer not using any
if we can.
Making the decision in practice
Let’s illustrate this with one more example that draws heavily from my explanation of primary associated types. You’ll want to read that first to fully understand this example:
class MusicPlayer {
var playlist: any Collection<String> = []
func play(_ playlist: some Collection<String>) {
self.playlist = playlist
}
}
In this code, I use some Collection<String>
instead of writing func play<T: Collection<String>>(_ playlist: T)
because the generic is only used in one place.
My var playlist
is an any Collection<String>
and not a some Collection<String>
for two reasons:
- There would be no way to ensure that the concrete collection that the compiler will deduce for the
play
method matches the concrete collection that’s deduced forvar playlist
; this means they might not be the same which would be a problem. - The compiler can’t deduce what
var playlist: some Collection<String>
in the first place (try it, you’ll get a compiler error)
We could avoid any
and write the following MusicPlayer
:
class MusicPlayer<T: Collection<String>> {
var playlist: T = []
func play(_ playlist: T) {
self.playlist = playlist
}
}
But this will force us to always use the same type of collection for T
. We could use a Set
, an Array
, or another Collection
but we can never assign a Set
to playlist
if T
was inferred to be an Array
. With the implementation as it was before, we can:
class MusicPlayer {
var playlist: any Collection<String> = []
func play(_ playlist: some Collection<String>) {
self.playlist = playlist
}
}
By using any Collection<String>
here we can start out with an Array
but pass a Set
to play
, it’s all good as long as the passed object is a Collection
with String
elements.
In Summary
While some
and any
sound very complex (and they honestly are), they are also very powerful and important parts of Swift 5.7. It’s worth trying to understand them both because you’ll gain a much better understanding about how Swift deals with generics and protocols. Mastering these topics will really take your coding to the next level.
For now, know that some
or generics should be preferred over any
if it makes sense. The any
keyword should only be used when you really want to use that existential or box type where you’ll need to peek into the box at runtime to see what’s inside so you can call methods and access properties on it.