Any vs AnyObject vs NSObject

What is the difference between these three enigmatic types? A sometimes confusing topic, let’s get it straight.

UPDATE: Things have changed in Swift 3!

This post has been updated for Swift 3 here.

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:

//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 two tricks for removing this error:

Solution 1: Import UIKit

If you import UIKit the error goes away. Why?

import UIKit
//No error, test inferred to be [NSObject]
var test = ["a",0]

UIKit Framework includes the Foundation framework which automatically bridges common data types to their Foundation Objective-C counterparts. And 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?

import Foundation
class Test {}
//No error, test inferred to be NSArray
var test = [Test(),"a",0]

The compiler can’t find a root class to infer the array’s data type, but instead of displaying an error, it instead infers an alternative data type – the Foundation Objective-C NSArray. NSArray is less strict than its Swift countertype; NSArray doesn’t enforce that elements it contains are the same data type.

Solution 2: [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.

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:

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.

//Error: String does not conform to element type 'AnyObject'
var test:[AnyObject] = ["a",0]

Unless you import Foundation! (or UIKit) As we saw earlier, importing UIKit bridges Foundation data types to their Foundation counterparts. Strings are bridged to NSStrings and Ints are bridged to NSNumbers. NSNumbers and NSStrings happen to be defined as classes in Objective-C, so they now fulfill the definition of being an AnyObject:

import UIKit
//No error: test defined as [AnyObject]
var test:[AnyObject] = ["a",0]

Interactive developer, Author - iOS development with Swift - book coming 2017

Tagged with:
Posted in Swift

Leave a Reply

Please log in using one of these methods to post your comment: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: