AnyObject
and Any
got a new option any
as introduced in SE-355, making it harder for us developers to know the differences. Each option has its use cases and pitfalls regarding when not to use them.
Any
and AnyObject
are special types in Swift, used for type erasure, and don’t have a direct relationship with any
. Be aware of the uppercase A
in this article since I’ll cover both Any
and any
which have different explanations. Let’s dive into the details!
When to use AnyObject
?
AnyObject
is a protocol to which all classes implicitly conform. In fact, the standard library contains a type alias AnyClass
representing AnyObject.Type
.
print(AnyObject.self) // Prints: AnyObject
print(AnyClass.self) // Prints: AnyObject.Type
All classes, class types, or class-only protocols can use AnyObject
as their concrete type. To demonstrate, you could create an array of different types:
let imageView = UIImageView(image: nil)
let viewController = UIViewController(nibName: nil, bundle: nil)
let mixedArray: [AnyObject] = [
// We can add both `UIImageView` and `UIViewController` to the same array
// since they both cast to `AnyObject`.
imageView,
viewController,
// The `UIViewController` type conforms implicitly to `AnyObject` and can be added as well.
UIViewController.self
]
Only classes conform to AnyObject
, meaning that you can use it to restrict protocols to be implemented by reference types only:
protocol MyProtocol: AnyObject { }
You can use AnyObject
if you need the flexibility of an untyped object. I could give examples of using a collection of any objects that you can cast back to a concrete type when used, but I would like to suggest something different instead.
I can’t remember using AnyObject
in any of my projects, since I was always able to use concrete types instead. Doing so also leads to more readable code that is easier to understand by fellow developers. To demonstrate this to you, I’ve created this example method which configures an image into a destination container:
func configureImage(_ image: UIImage, in imageDestinations: [AnyObject]) {
for imageDestination in imageDestinations {
switch imageDestination {
case let button as UIButton:
button.setImage(image, for: .normal)
case let imageView as UIImageView:
imageView.image = image
default:
print("Unsupported image destination")
break
}
}
}
By using AnyObject
as our destination, we always need to cast and consider casting failures using the default implementation. My preference would always be to rewrite this using concrete protocols instead:
// Create a protocol to act as an image destination.
protocol ImageContainer {
func configureImage(_ image: UIImage)
}
// Make both `UIButton` and `UIImageView` conform to the protocol.
extension UIButton: ImageContainer {
func configureImage(_ image: UIImage) {
setImage(image, for: .normal)
}
}
extension UIImageView: ImageContainer {
func configureImage(_ image: UIImage) {
self.image = image
}
}
// Create a new method using the protocol as a destination.
func configureImage(_ image: UIImage, into destinations: [ImageContainer]) {
for destination in destinations {
destination.configureImage(image)
}
}
The resulting code is cleaner, readable, and no longer requires handling unsupported containers. Instances are required to conform to the ImageContainer
protocol to be able to receive the configured image.
When to use Any
?
Any
can represent an instance of any type at all, including function types:
let arrayOfAny: [Any] = [
0,
"string",
{ (message: String) -> Void in print(message) }
]
To me, the same rules apply to Any
compared to AnyObject
meaning that you should always seek to use concrete types instead. Any
is more flexible by allowing you to cast instances of any type, making code harder to predict compared to using concrete types.
When to use any
?
any
is introduced in SE-335 and looks similar to Any
and AnyObject
but has a different purpose since you use it to indicate the use of an existential. The following example code demonstrates an image configurator using the sample code from previous examples in this article:
struct ImageConfigurator {
var imageContainer: any ImageContainer
func configureImage(using url: URL) {
// Note: This is not the way to efficiently download images
// and is just used as a quick example.
let image = UIImage(data: try! Data(contentsOf: url))!
imageContainer.configureImage(image)
}
}
let iconImageView = UIImageView()
var configurator = ImageConfigurator(imageContainer: iconImageView)
configurator.configureImage(using: URL(string: "https://picsum.photos/200/300")!)
let image = iconImageView.image
As you can see, we indicated the use of an existential ImageContainer
by marking our imageContainer
property with the any
keyword. Marking a protocol using any
will be enforced starting from Swift 6 as it will indicate the performance impact of using a protocol in this way.
Existential types have significant limitations and performance implications and are more expensive than using concrete types since you can change them dynamically. The following code is an example of such change:
let button = UIButton()
configurator.imageContainer = button
Our imageContainer
property can represent any value conforming to our ImageContainer
protocol and allows us to change it from an image view to a button. Dynamic memory is required to make this possible, taking away the possibility for the compiler to optimize this piece of code. Up until the introduction of the any
keyword, there was no explicit indication to developers indicating this performance cost.
Moving away from any
In a way, you could argue any
, Any
, and AnyObject
have something in common: use them with caution.
You could rewrite the above code example by using generics and take away the need for dynamic memory:
struct ImageConfigurator<Destination: ImageContainer> {
var imageContainer: Destination
}
Being aware of the performance implications and knowing how to rewrite the code instead is an essential skill to own as a Swift developer.
Conclusion
Any
, any
, and AnyObject
look similar but have important differences. In my opinion, it’s better to rewrite your code and take away the need to use any of these keywords. Doing so often results in more readable and predictable code.
If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.
Thanks!