Every now and then, I take some time out of my day to explore something about the Swift language that I don't know yet. There is a lot left to explore. I document my findings in a series I named What The Swift. If you are curious about the more subtle details of the Swift language, then this series is for you.
In the previous episode of this series, we discovered that AnyObject
is a protocol defined in the Swift standard library. In today's installment of "What The Swift", I want to show you what Any
is. To discover the meaning of Any
, we need to revisit the Swift standard library.
Get Ready to Play
Open Xcode and and create a new playground. Choose the Blank template from the iOS section.
Name the playground What The Swift, tell Xcode where you'd like to save the playground, and click Create.
Revisiting the Swift Standard Library
Clear the contents of the playground and declare a variable, myObject
, of type Any
.
var myObject: Any
This time Xcode doesn't give us a clue as to the meaning of Any
. Earlier this week, we found out that AnyObject
is a protocol. The same doesn't seem to be true for Any
. Xcode's autocompletion menu shows that AnyObject
is a protocol.
The documentation is vague about the meaning of Any
. It looks like we need to dig a bit deeper.
Press Command and click Any
in the playground to jump to the definition of Any
. This worked fine for AnyObject
, but it doesn't seem to work for Any
. Does this mean that Any
isn't defined in the Swift standard library?
The Swift Programming Language
Let's take a look at The Swift Programming Language to find out more about Any
. In the chapter titled Type Casting, we find the following.
Any
can represent an instance of any type at all, including function types.
That's a start. It tells us that the meaning of Any
is broader than that of AnyObject
. Any
can represent anything hence the name. This also explains why Any
isn't a protocol. Even though we know what Any
is, I'd like to know more about it. That's the goal of this episode.
Any and id
A Google search brings us to an article on Apple's Swift blog. The title, Objective-C id
as Swift Any
, is very telling. The article discusses the meaning of Any
and AnyObject
. More importantly, it explains that the meaning of AnyObject
and Any
have changed with the release of Swift 3.
In Swift 3, the
id
type in Objective-C now maps to theAny
type in Swift, which describes a value of any type, whether a class, enum, struct, or any other Swift type.
This change improves the compatibility of Swift and Objective-C. Objective-C collections, for example, can hold elements of Any
type as of Swift 3. This means that Foundation's NSArray
type can store String
, Float
, and Int
instances.
But I want to know more about the why and the how. The article references three Swift Evolution proposals, SE-0072, SE-0116, and SE-0131. It's SE-0116 that looks most promising.
SE-0116
SE-0116 describes in detail the why and the how of this change. The gist is plain and simple. To improve the compatibility of Objective-C and Swift, Objective-C's id
type maps to Swift's Any
type.
In Swift 2, id
mapped to AnyObject
. While this worked fine, it was far from ideal. Not only did it sometimes result in unexpected behavior, the solution didn't do justice to one of Swift's fundamental concepts, value types. Objective-C and Swift are very different if you take a close look at both languages.
The change introduced by SE-0116 means that it should be possible to bridge any Swift type to an Objective-C object. This isn't a problem for classes and bridged value types, such as Int
, String
, and the collection types defined by the Swift standard library. But there are plenty of value types that don't have an Objective-C counterpart. These value types are bridged as opaque objects, instances of an immutable class. They are opaque because the name and behavior of these classes are not revealed.
What Is AnyHashable
But there's one problem. Even though Any
can represent an instance of any type, it has its limitations. You probably know that the elements of a set and the keys of a dictionary need to be hashable. They need to conform to the Hashable
protocol. A set needs the ability to uniquely identify each of the elements it contains. And to look up the value associated with a given key, a dictionary requires its keys to be unique. The problem is that Any
doesn't meet this requirement.
As of Swift 3, the Swift standard library defines the AnyHashable
supertype. This means that types conforming to the Hashable
protocol can be used as AnyHashable
. In the Swift standard library, AnyHashable
is defined as a struct.
public struct AnyHashable {
public init<H>(_ base: H) where H : Hashable
public var base: Any { get }
}
The AnyHashable
supertype is used to bring untyped sets and dictionaries from Objective-C to Swift. For example, the userInfo
property of Foundation's Notification
type is of type [AnyHashable:Any]
.
public struct Notification : ReferenceConvertible, Equatable, Hashable {
...
/// Storage for values or objects related to this notification.
public var userInfo: [AnyHashable : Any]?
...
}
What The Swift
Once you understand the meaning of Any
and AnyHashable
, much of the mystery disappears. I hope this episode has also shown you the value of the Swift Evolution project. Many Swift Evolution proposals are submitted by members of the Swift community, resulting in a better, more robust language.