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
Andrii H.
  • 151
  • 1
  • 1
  • 3
  • You can not have instances of a protocol directly, but only structs/classes that conform to it. – Shripada Sep 30 '15 at 09: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)]` – Adam Sep 30 '15 at 09:23

4 Answers4

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.

Maic López Sáenz
  • 10,385
  • 4
  • 44
  • 57
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 :(

Community
  • 1
  • 1
Błażej
  • 3,617
  • 7
  • 35
  • 62
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.

Beraliv
  • 518
  • 7
  • 20
  • 1
    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 Sep 30 '15 at 09:22
  • @Grimxn i understood after I saw the actual answer. – Beraliv Oct 06 '15 at 11:03
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

Sentry.co
  • 5,355
  • 43
  • 38