Give your simulator superpowers

Give your Xcode
Simulator extra features

AnyObject, Any, and any: When to use which?

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!

 

Featured SwiftLee Jobs

Loading RSS Feed

Browse more Swift related Jobs, or add your own on SwiftLee Jobs