15

There is a protocol:

protocol Valuable {
    func value() -> Int
}

and a class which implements the protocol:

class Value: Valuable {
    private let v: Int

    init(value: Int) {
        v = value
    }

    func value() -> Int {
        return v
    }
}

There is an array of Value objects stored in a variable of Any type:

let any: Any = [Value(value: 1), Value(value: 2), Value(value: 3)]

It is possible to cast Any to [Value]:

let arrayOfValue = any as? [Value] // [1, 2, 3]

Why it is not possible to case Any to [Valuable]?

let arrayOfValuable = any as! [Valuable] // compiler error BAD INSTRUCTION
let arrayOfValuable2 = any as? [Valuable] // nil
2
  • You can not have instances of a protocol directly, but only structs/classes that conform to it. Commented Sep 30, 2015 at 9:12
  • Not sure why the cast is not working but maybe declaring it as [Valuable] in the first place would be enough as a workaround for you: let any : [Valuable] = [Value(value: 1), Value(value: 2), Value(value: 3)] Commented Sep 30, 2015 at 9:23

4 Answers 4

5

Updated: In Swift3 it is entirely possible to cast [Any] to a [Valuable]. The cast will succeed as long as all the elements in the array can be casted; the cast will fail otherwise.

var strings: [Any] = ["cadena"]
var mixed: [Any] = ["cadena", 12]

strings as! [String] // ["cadena"]
mixed as? [String] // nil
mixed as! [String] // Error! Could not cast value...

Previously as of Swift 2: To make a [Valuable] out of an [Any] it must be done manually with functions like map as other answers have explained.

There is currently no covariance nor contravariance with generics in Swift (as of Swift 2). This means that arrays of different types, like [String] or [UIView], cannot be casted into each other, nor their types compared.

[UIView] and [UIButton] bear no hierarchy between each other regardless that UIButton is a subclass of UIView. This is why even if the following returns true:

Valuable.self is Any.Type // true

the following casts yield errors for the same reason:

var anyArray: [Any] = ["cadena"]

anyArray as! [String] // BAD_INSTRUCTION error
"some string" as! Double // BAD_INSTRUCTION error

The two clases bear no relation and the cast is impossible, since the as! is a forced cast it booms the error.

Sign up to request clarification or add additional context in comments.

Comments

3

I do some dig and you have to add @objc attribute as follows

@objc
protocol Valuable {
    func value() -> Int
}

class Value: Valuable {
    private let v: Int

    init(value: Int) {
        v = value
    }

    @objc func value() -> Int {
        return v
    }
}

let any: AnyObject = [Value(value: 1), Value(value: 2), Value(value: 3)]

let arrayOfValueable = any as! [Valuable] // [{v 1}, {v 2}, {v 3}]

For more info and to get anwser for "why?": https://stackoverflow.com/a/25589323/989631

Hope this will help you.

Edit

Plus it works only if you use AnyObject instead of Any :(

Comments

2

It works for me:

let arrayOfValuable = arrayOfValue?.map { $0 as Valuable }

or the same:

let arrayOfValuable2 = (any as? [Value])?.map { $0 as Valuable }

In conclusion, arrayOfValuable should have the type of [Valuable]?

Edit:

Or try this:

let arrayOfAny: [Any] = [Value(value: 1), Value(value: 2), Value(value: 3)]
let arrayOfValuable3 = arrayOfAny.map { $0 as Valuable }

Sure, better way to do that is to declare arrayOfAny as [Valuable], so you'll have no problems afterwards.

2 Comments

That's not the same - you are casting to Value in the second map, not Valuable, which is what the question asks. Replace as? [Value] with as? [Valuable] and you get nil, as OP says...
@Grimxn i understood after I saw the actual answer.
0

Casting Array[Any] to Array[String]

let arrayOfAny:Array<Any> = [1,"2",3.0,CGFloat(4)]
let stringArray:Array<String> = arrayOfAny.map {String($0)}
print(stringArray)//"1", "2", "3.0","4.0"

Conclusion:
Sometimes its useful to convert from one array type to another. The best approach is in most cases not to convert the array type but to either do instance checking before packing the array or instance checking after unpacking the array

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.