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.
If you've spent some time writing Swift, then you've probably come across a variable, constant, or parameter of type AnyObject
. And you know what AnyObject
is. Right? Don't worry. You're not alone. Let's take a look and dive into the bowels of the Swift standard library to find out.
Get Your Hands Dirty
Fire up Xcode 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.
Exploring the Swift Standard Library
Clear the contents of the playground and declare a variable, myObject
, of type AnyObject
.
var myObject: AnyObject
If you pay close attention, you can see a first clue. Take a look at Xcode's autocompletion suggestions when you type AnyObject
.
Did you notice the letter T on the left? Xcode's autocompletion menu shows us that AnyObject
is a type. That isn't surprising. But there's more. Xcode also tells us that AnyObject
is a protocol to which all classes implicitly conform. That's a start. Let's dive into the bowels of the Swift standard library to learn more about the AnyObject
protocol.
Press Command and click AnyObject
to navigate to the interface of the AnyObject
protocol. There isn't a lot of code to explore. AnyObject
is defined as a public type alias.
public typealias AnyObject
More interesting are the comments above the definition. The comments are very telling and answer some of the questions you might have.
The protocol to which all classes implicitly conform.
This summarizes what the AnyObject
protocol is and what you need to know about it. Every class implicitly conforms to the AnyObject
protocol. That's an interesting statement. But what does it mean? And why is that useful or important? Let's revisit the example we added to the playground earlier.
var myObject: AnyObject
Because the myObject
variable is of type AnyObject
, we know that the value stored in myObject
is an instance of a class. And that's also what the comments of the AnyObject
protocol tell us.
You use
AnyObject
when you need the flexibility of an untyped object or when you use bridged Objective-C methods and properties that return an untyped result.AnyObject
can be used as the concrete type for an instance of any class, class type, or class-only protocol.
If you're familiar with Objective-C, then you've most likely come across the id
keyword. The id
type is used to point to an Objective-C object. The AnyObject
protocol is similar and it helps bridge the gap between Swift and Objective-C. The Swift standard library confirms this.
The flexible behavior of the
AnyObject
protocol is similar to Objective-C'sid
type. For this reason, imported Objective-C types frequently useAnyObject
as the type for properties, method parameters, and return values.
Downcasting AnyObject
The AnyObject
protocol seems to add a bit of flexibility to the Swift language. But we need to pay a small price for that flexibility. A class instance of type AnyObject
isn't always what you need or want. Take a look at this example.
class Person {
var first: String = ""
var last: String = ""
}
let myPerson: AnyObject = Person()
We define a class, Person
, with two variable properties of type String
, first
and last
. We create an instance of the Person
class and assign it to the myPerson
constant. Even though the compiler can infer the type of the myPerson
constant, we explicitly declare myPerson
as AnyObject
. The compiler doesn't complain since the Person
class implicitly conforms to the AnyObject
protocol.
We also learned that AnyObject
can be used as the concrete type for an instance of any class, class type, or class-only protocol. But remember that AnyObject
is a protocol, not a class type.
Even though the example is a bit contrived, it illustrates why the AnyObject
protocol can be inconvenient to use. Because myPerson
is of type AnyObject
, we cannot access the first
and last
properties of the Person
instance.
Despite myPerson
being of type AnyObject
, it remains a Person
instance. This is how the Swift standard library phrases this.
Objects with a concrete type of
AnyObject
maintain a specific dynamic type and can be cast to that type using one of the type-cast operators (as
,as?
, oras!
).
To access the properties and methods of the Person
instance, we need to downcast myPerson
to a Person
instance. This means we type cast myPerson
to the Person
type using a downcast operator. In this example, we use optional binding and the conditional downcast operator to safely downcast myPerson
to the Person
type.
class Person {
var first: String = ""
var last: String = ""
}
let myPerson: AnyObject = Person()
if let person = myPerson as? Person {
person.first
person.last
}
Because the person
constant is of type Person
, we can access the first
and last
properties of the Person
instance. Always remember that a downcast operation can fail. That's why the conditional downcast operator returns an optional.
The AnyObject
protocol is also useful to bridge the gap between Swift and Objective-C. Some Objective-C APIs use the AnyObject
protocol to provide compatibility with Swift.
What The Swift
I hope this exploration has taught you more about the AnyObject
protocol, what it is, and what it can be used for. Next time you encounter a variable, constant, or parameter of type AnyObject
you know what it is and how to handle it.