1

We all know that swift has a strong type system, and as such I lean towards using this system to my advantage :)

Here's what Apple has to say about using the Any type:

Type Casting for Any and AnyObject

Swift provides two special type aliases for working with non-specific types:

AnyObject can represent an instance of any class type. Any can represent an instance of any type at all, including function types. NOTE

Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work with in your code.

I don't know if it's me, but I kind of think the code starts to smell when using these (This maybe my lack of experience using them) - I know AnyObject is required/useful for interacting with Objective-C so I get that there is a practical nature to AnyObject's use.

So I wanted to know what concrete examples represent a good use of the Any type.

For example is it could be used to pass unknown content to a POST request constructor method where the method could safely use optional chaining to examine the unknown content.

Apple's example from the Swift Programming Language (Swift 2.2)

Any

Here’s an example of using Any to work with a mix of different types, including function types and non-class 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(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
Peter Hornsby
  • 4,208
  • 1
  • 25
  • 44

2 Answers2

2

If you are using pure Swift (without the legacy Objective-C stuff) and you want an array where you can put Int(s), Bool(s) and String(s) then you need an array of Any.

let values: [Any] = [1, true, "I'm not a pointer!"]

enter image description here

But, wait I can also use an array of AnyObject right?

Nope. Infact Int, Bool and String in Swift are struct(s).

They why does this code compile?

enter image description here

The code above compile because the import Foundation does enable the bridge to Objective-C. This mean the Swift Int, Bool and String are allowed to be seen as objects by the compiler in order to preserve compatibility with Objective-C.

But as soon as you remove the import Foundation the code stop compiling.

enter image description here

Luca Angeletti
  • 58,465
  • 13
  • 121
  • 148
2

When should I use [Any]?

Any is an abstract type that all other types implicitly conform to. Because it could be anything – it must be guaranteed to do nothing.

For that very reason, typing a variable as Any is nonsensical. If I define this:

let itCouldBeAnything : Any = "Actually it's a string"

It now can't do anything (without type casting):

enter image description here

You should always use the most descriptive type available when defining something, and because of Any's abstract nature, there will always be a more descriptive type available – in this case, String should've been used.

Now let’s think about arrays. As you say, Apple demonstrates that you can use an array of Any to mix together different types.

var things = [Any]()

things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))

Great. But what does this array actually represent? It’s just a collection of junk – the programming equivalent of the backseat of my car.

If we try and do something with one of the elements...

enter image description here

We can’t. It could be anything – so we can’t do anything with it. Sure, we can type cast the elements with a switch or if statement to handle the different possibilities – but all we’ll succeed in doing there is splitting our array back into distinct types. So what was the point in using [Any] to begin with?

An array should always represent a collection of related data. There’s no point of an array if the elements share nothing in common. If the different types that the array can store have common functionality, then you should define a protocol with this common functionality:

protocol ACommonProtocol {
    // define common functionality
}

We can then conform the types that the array can store to it:

extension Int : ACommonProtocol {}
extension Double : ACommonProtocol {}
extension String : ACommonProtocol {}
extension Movie : ACommonProtocol {}

Now our array can be of type [ACommonProtocol]. This is a significant improvement from Any, we’ve narrowed the concrete types that the elements could be down to 4 – and we can now use any common functionality that we define in the protocol on the elements without type-casting. Our elements also are now explicitly related, which actually gives the array some meaning (assuming the protocol represents a meaningful concept).

If the different types that the array can store don't have any common functionality, or cannot conform to protocols (such as tuples and functions), but are still related in some meaningful way – then another way of expressing this relationship is through using an enum with associated values:

// obviously this enum (and possibly its cases) should have a far better names
enum IntDoubleStringOrMovie { 
    case int(Int)
    case double(Double)
    case string(String)
    case movie(Movie)
}

Now our array can be of type [IntDoubleStringOrMovie] – which has the added benefit of allowing us to use an exhaustive switch in order to determine the element type:

for element in array {
    switch element {
    case let .int(int):
        print(int)
    case let .double(double):
        print(double)
    case let .string(string):
        print(string)
    case let .movie(movie):
        print(movie)
    }
}

So for this reason, you should never ever have to use [Any] in Swift. If you ever find yourself in a situation of using [Any], you should be re-considering your data structure. Whether that involves splitting the array up into sub-arrays, conforming the elements to a common protocol (if they have common functionality), using an enum with associated values (if they have no common functionality) or using a different collection type altogether, is entirely up to you.

But what about interfacing with Objective-C?

Any is sometimes necessary for bridging with Objective-C’s inferior type system – and therefore you may well find yourself using [Any] with Objective-C APIs, which is acceptable (although with the bridging of Objective-C's lightweight generics to Swift, it should be uncommon).

The only thing I would say about this is once you’re back in your Swift logic, you should always convert an [Any] back to a meaningful array type through type casting, or unpacking it into a more appropriate data structure. You shouldn’t sacrifice type safety in Swift just because Objective-C lacks in that regard.

But what about [AnyObject]?

AnyObject is pretty much as vague as Any in pure Swift. The only thing it guarantees is that it’s a class. However, as of Swift 3, anything can be bridged to Objective-C by being boxed in a _SwiftValue. For this reason, [AnyObject] is just as (if not more) questionable as [Any].

Community
  • 1
  • 1
Hamish
  • 78,605
  • 19
  • 187
  • 280