Swift AnyObject, AnyClass, Any, and `any`

Updated Jan 11, 2024#swift#types

Swift provides serveral special types for working with nonspecific types:

  • AnyObject represents an instance of any class type.
  • AnyClass represents a class type rather than an instance of that class.
  • Any represents an instance of any type at all, including function types.
  • any represents existential types, can only be applied to protocols and protocol compositions, or metatypes thereof.

and some tools to work with nonspecific types:

Nonspecific types can be useful in certain scenarios because they provide a lot of flexibility in working with different types of data. Here are some potential benefits and use cases:

  • Flexible data handling. Useful in situations where you need to work with a wide range of data types that might not be known in advance like working with a JSON API that can return data in a variety of formats.
  • Dynamic dispatch. Where you need to perform dynamic dispatch based on the actual type of an object at runtime.
  • Interoperability with Objective-C. Because Objective-C doesn’t have the same type safety features as Swift, like working with an Objective-C library that returns instances of id.

Because nonspecific types can represent any type of data, it’s possible to accidentally pass the wrong type of data to a function or method. This can lead to runtime errors or unexpected behavior.

It’s a good idea to use more specific types whenever possible, as they can provide increased type safety and make your code easier to reason about.

AnyObject

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.

The flexible behavior of the AnyObject protocol is similar to Objective-C’s id type. For this reason, imported Objective-C types frequently use AnyObject as the type for properties, method parameters, and return values.

class MyClass {
    var myProperty: String
    
    init(myProperty: String) {
        self.myProperty = myProperty
    }
}

class MyOtherClass {
    var myOtherProperty: Int
    
    init(myOtherProperty: Int) {
        self.myOtherProperty = myOtherProperty
    }
}

let instanceOfMyClass = MyClass(myProperty: "Hello, world!")
let instanceOfMyOtherClass = MyOtherClass(myOtherProperty: 42)

let array: [AnyObject] = [instanceOfMyClass, instanceOfMyOtherClass]

for object in array {
    if let myClassInstance = object as? MyClass {
        print("Instance of MyClass with property: \(myClassInstance.myProperty)")
    } else if let myOtherClassInstance = object as? MyOtherClass {
        print("Instance of MyOtherClass with property: \(myOtherClassInstance.myOtherProperty)")
    }
}

// Prints:
// Instance of MyClass with property: Hello, world!
// Instance of MyOtherClass with property: 42

Because we’re using AnyObject, we’re able to store objects of different types in the same array. However, we need to use conditional casts to determine the specific type of each object in the array in order to work with its properties.

AnyClass

typealias AnyClass = AnyObject.Type

You can use the AnyClass protocol as the concrete type for an instance of any class. When you do, all known @objc class methods and properties are available as implicitly unwrapped optional methods and properties, respectively.

import Foundation

class IntegerRef {
    @objc class func getDefaultValue() -> Int {
        return 42
    }
}
 
func getDefaultValue(_ c: AnyClass) -> Int? {
    return c.getDefaultValue?()
}
print(getDefaultValue(IntegerRef.self))
// Prints "Optional(42)"
 
print(getDefaultValue(NSString.self))
// Prints "nil"

Any

In Swift, Any can represent an instance of any type at all, including function types, broader than AnyObject. This also explains why Any isn’t a protocol.

Here’s an example of using Any to work with a mix of different types, including function types and nonclass types. The example creates an array called things, which can store values of type Any:

var things: [Any] = []

things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append({ (name: String) -> String in "Hello, \(name)" })

any

In Swift, a plain protocol name in type context means an existential type. They have significant limitations and performance implications, and also significantly more expensive than using concrete types. In addition to heap allocation and reference counting, code using existential types incurs pointer indirection and dynamic method dispatch that cannot be optimized away.

Since Swift 5.6, anywhere that an existential type can be used, the any keyword can be used to explicitly denote an existential type:

// Swift 5 mode

protocol P {}
protocol Q {}
struct S: P, Q {}

let p1: P = S() // 'P' in this context is an existential type
let p2: any P = S() // 'any P' is an explicit existential type

let pq1: P & Q = S() // 'P & Q' in this context is an existential type
let pq2: any P & Q = S() // 'any P & Q' is an explicit existential type

Explicit any can only be applied to protocols and protocol compositions, or metatypes thereof; any cannot be applied to nominal types, structural types, type parameters, and protocol metatypes:

struct S {}

let s: any S = S() // error: 'any' has no effect on concrete type 'S'

func generic<T>(t: T) {
  let x: any T = t // error: 'any' has no effect on type parameter 'T'
}

let f: any ((Int) -> Void) = generic // error: 'any' has no effect on concrete type '(Int) -> Void'

Using any Any and any AnyObject are redundant. Any and AnyObject are already special types in the language, and their existence isn’t nearly as harmful as existential types for regular protocols because the type-erasing semantics is already explicit in the name.