Tuesday 20 November 2018

Any vs AnyObject in Swift

Difference between Any vs AnyObject in Swift

What is the difference between these three enigmatic types? A sometimes confusing topic, and to confuse things further Swift 3 has shaken it up by removing implicit bridging between Foundation and native data types. 
Let’s look at the current situation with a good old Venn diagram:
AnyAnyObject
Let’s take a closer look at distinguishing these types with an example using arrays.
You probably know Swift is a type-safe language. For this reason, the compiler won’t permit you to type infer an array of different types that don’t share a common root:
1
2
//Error: Type of expression is ambiguous without more context
var test = ["a",0]
Strings and Ints in Swift don’t share a common root, so the compiler doesn’t know what you want it to do when type inferring the array.
There are three tricks for removing this error:

Solution 1: Array of NSObjects

If you import UIKit and cast the string as an NSString and the Int as an NSNumber the error goes away. Why?
1
2
3
import UIKit
//No error, test inferred to be [NSObject]
var test =  ["a" as NSString,0 as NSNumber]
UIKit Framework includes the Foundation framework. If you have imported the Foundation framework you can explicitly bridge common Swift data types to their Foundation Objective-C counterparts. (this bridging was automatic pre-Swift 3) String for example, bridges to NSString and Int can bridge to NSNumber.
Unlike in Swift, in Objective-C, most data types do have a root: NSObject is an actual class (docs here), that is the root of most classes if you’re using the Foundation framework.
If you option-click on the test variable, you’ll find that it has defaulted to [NSObject].
But then – out of curiosity – what happens if you add another variable to the array that does not have NSObject as a root, such as a class of your own?
1
2
3
class Test {}
//Error
var test = [Test(),"a" as NSString,0 as NSNumber]
The compiler can no longer find a root class to infer the array’s data type. You would need a way to indicate to the compiler that you know that the elements of the array are incompatible, and you’re ok with that.

Solution 2: NSArray

One solution would be to type the array as Foundation data type NSArray. NSArray is less strict than its Swift countertype; NSArray doesn’t enforce that elements it contains are the same data type.
1
2
//No error: test defined as NSArray
var test:NSArray =  [Test(), "a",0]

Solution 3: [Any] or [AnyObject]

If you specifically define the array as [Any], you are indicating to the compiler that you are aware that the elements are not of the same data type, and you are ok with that.
1
2
3
class Test {}
//No error, test is defined as [Any]
var test:[Any] = [Test(),"a",0]
You may be surprised to learn that unlike NSObject, Any is not actually a concrete data type. You won’t find a type description for it in documentation. Rather Any is an alias for any data type.
Similarly, AnyObject is an alias for any data type derived from a class. You can use it to define an array that contains more than one object derived from a class, that don’t share a common root class:
1
2
3
4
class Test {}
class Test2 {}
//No error, test is defined as [AnyObject]
var test:[AnyObject] = [Test(),Test2()]
(You could of course have used [Any] as well – [AnyObject] is just a little more specific.
Passing in a string and an integer for example, to an AnyObject array will cause an error, as these data types are structs in Swift.
1
2
//Error: String does not conform to element type 'AnyObject'
var test:[AnyObject] = ["a",0]
Of course, an Array which could contain any data type is unlikely to pop up in your code, and if it does, maybe you should double-check you’re following best practices!
Rather, this has been an exercise to explore Any, AnyObject, NSObject, NSArray and how Swift 3 now requires explicit bridging between Foundation and native data types. Hopefully this helps

No comments:

Post a Comment

Please comment here...

203 Favorite JavaScript Utilities

https://1loc.dev/