Any vs. AnyObject in Swift 3.0
When I encountered these two type aliases while parsing JSON data for the first time, I had no idea how to distinguish or implement them properly. So, what are they? Any and AnyObject are two special types in Swift that are used for working with non-specific types.
According to Apple’s Swift documentation,
- Any can represent an instance of any type at all, including function types and optional types.
- AnyObject can represent an instance of any class type.
Okay, simple enough — Any is used for all types, AnyObject is used for Class types, right?
To understand how they really behave in code, I decided to play with them in a Playground.
Any allowed me to work with a mix of different types including function and non-class types such as Int, String, and Bool. According to the documentation, the elements in this array are Structs that are value types, so in theory AnyObject shouldn’t work in these cases.
To verify this I attempted to include Strings and Ints, which are value types in Swift, using AnyObject.
As expected, the compiler threw me an error saying that the elements did not conform to the type AnyObject in the array. Gotcha!
Then, this strange thing happened when I tried to follow the compiler’s suggestions:
What just happened?! How was I able to use AnyObject on Ints and Strings by explicitly casting each element to AnyObject?
I then printed the anyObjectArray into console.
The element Hi obviously looked like a string to me, but it did not have quotes around like a normal String value in Swift!
Next I printed each element using a for-in loop to check its actual type rather than its casted-type of AnyObject.
First, I used is operator to see whether the elements are Swift Struct types or not.
It is of type String! Then how could it be casted to AnyObject? Again, Strings in Swift are Structs, not Class types. Thus in theory, I shouldn’t be able to cast them as AnyObject.
I was utterly confused, and decided to do some more experiments with it. This time I used NSNumber and NSString, which are Objective-C types, to check the type of each element.
Wait, Hi is also a NSString and numeric elements are NSNumber! And… they are reference types in Objective-C! Was this the reason why Hi didn’t have quotes on it in console? I wrote some more code as below to see if my assumption was correct. 🔎
Confirmed! The elements that are casted to AnyObject in the array are now class types of Objective-C: NSString and NSNumber.
So… What’s really going on under the hood? I continued digging into this topic and found the most plausible answer from the document Using Swift with Cocoa and Objective-C (Swift 3.0.1) .
As part of its interoperability with Objective-C, Swift offers convenient and efficient ways of working with Cocoa frameworks. Swift automatically converts some Objective-C types to Swift types, and some Swift types to Objective-C types. Types that can be converted between Objective-C and Swift are referred to as bridged types.
Anywhere you can use a bridged Objective-C reference type, you can use the Swift value type instead. This lets you take advantage of the functionality available on the reference type’s implementation in a way that is natural in Swift code. For this reason, you should almost never need to use a bridged reference type directly in your own code. In fact, when Swift code imports Objective-C APIs, the importer replaces Objective-C reference types with their corresponding value types. Likewise, when Objective-C code imports Swift APIs, the importer also replaces Swift value types with their corresponding Objective-C reference types.”
In other words, the compiler does its best to be flexible in handling such types through automatic conversion and bridging while preventing our app from crashing easily. Brilliant!
So when do we actually use AnyObject? As stated in Apple’s documentation, AnyObject can be used for working with objects that derive from Class but that don’t share a common root Class.
But is it absolutely necessary to use it in our code?
My answer to this question is: No.
In Swift 3, the id type in Objective-C now maps to the Any type in Swift, which describes a value of any type, whether a class, enum, struct, or any other Swift type. This change makes Objective-C APIs more flexible in Swift, because Swift-defined value types can be passed to Objective-C APIs and extracted as Swift types, eliminating the need for manual “box” types.
These benefits also extend to collections: Objective-C collection types NSArray, NSDictionary, and NSSet, which previously only accepted elements of AnyObject, now can hold elements of Any type. For hashed containers, such as Dictionary and Set, there’s a new type AnyHashable that can hold a value of any type conforming to the Swift Hashable protocol.
It seems that Any alone works perfectly fine in bridging these two languages in Swift 3 without the need for the use of AnyObject!
So what was the ultimate reasoning behind these changes?
In their own words, Apple explains:
Swift 3 interfaces with Objective-C APIs in a more powerful way than previous versions. For instance, Swift 2 mapped the id type in Objective-C to the AnyObject type in Swift, which normally can hold only values of class types. Swift 2 also provided implicit conversions to AnyObject for some bridged value types, such as String, Array, Dictionary, Set, and some numbers, as a convenience so that the native Swift types could be used easily with Cocoa APIs that expected NSString, NSArray, or the other container classes from Foundation. These conversions were inconsistent with the rest of the language, making it difficult to understand what exactly could be used as an AnyObject, resulting in bugs.
One would insist, however, that we iOS developers always need to be as specific as possible in terms of using types in code.
Indeed, Apple recommends:
Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work with in your code.
Think about this scenario: We are working with number 12.5 in Swift. In this case we would specifically state that it is a type of Double or Float rather than declare it of type Any. This way, we can conveniently access different properties or methods that are available for that specific type. In this context, we would use AnyObject for Class types because they are a little more specific than Any. But again, the use of AnyObject is just an option.
I hope this blog post helped many of you awesome developers out there on clarifying Any and AnyObject. Use Any confidently in Swift 3 while working with Objective-C backed APIs.
Thank you for reading and happy coding!