Declare array of classes that conform to a protocol

swift

Solution

"array of classes that conform to a protocol" can be declared like `Array<TheProtocol.Type>`.

You can:

var array: Array<ControllerConstructorProtocol.Type> = [
    MyConstructor.self,
    MyOtherConstructor.self,
]

But...,

    array[0].construct()
//  ^ error: accessing members of protocol type value 'ControllerConstructorProtocol.Type' is unimplemented

Calling method on the item is "unimplemented".

As of now, you have to declare the protocol as `@objc`, and call the method via `AnyClass`. Moreover, for some reasons, we cannot directly cast `array[0]` to `AnyClass`, instead, we have to cast it to `Any`, then `AnyClass`.

@objc protocol ControllerConstructorProtocol {
    class func construct() -> UIViewController?
}

var array: Array<ControllerConstructorProtocol.Type> = [
    MyConstructor.self,
    MyOtherConstructor.self,
]

let vc = (array[0] as Any as AnyClass).construct()

Note: Casting problem was fixed in Swift 1.2 / Xcode 6.3. But "unimplemented" is "unimplmented" :(

Just random ideas:

It's depends on your actual use-case, but in this particular case, array of `()-> UIViewController?` closures is sufficient:

var array: [() -> UIViewController?] = [
    MyConstructor.construct,
    MyOtherConstructor.construct,
]

let vc = array[0]()

If you have several methods, you might want to use type-erased wrapper of the protocol.

protocol ControllerConstructorProtocol {
    class func construct() -> UIViewController?
    class func whoami() -> String
}

struct ControllerConstructorWrapper {
    private let _construct: () -> UIViewController?
    private let _whoami: () -> String
    init<T: ControllerConstructorProtocol>(_ t:T.Type) {
        _construct = { t.construct() }
        _whoami = { t.whoami() }
    }
    func construct() -> UIViewController? { return _construct() }
    func whoami() -> String { return _whoami() }
}

var array: [ControllerConstructorWrapper] = [
    ControllerConstructorWrapper(MyConstructor),
    ControllerConstructorWrapper(MyOtherConstructor),
]

let who = array[0].whoami()
let vc = array[0].construct()

Problem

Lets say I have created this protocol and a couple of classes ``` import UIKit protocol ControllerConstructorProtocol { class func construct() -> UIViewController? } class MyConstructor: ControllerConstructorProtocol { class func construct() -> UIViewController? { return UIViewController() } } class MyOtherConstructor: ControllerConstructorProtocol { class func construct() -> UIViewController? { return UITableViewController(style: .Grouped) } } ``` Now I want to declare an array that contains classes of objects that will conform to such protocol. How can I declare it? Ideally I would like the compiler to check the array is correctly filled (at compile time) rather than checking it myself at (run time) with the `as` operator. This is what I have tried without success :( This leads to a compile error: 'Any Object does not have a member named 'construct' ``` var array = [ MyConstructor.self, MyOtherConstructor.self, ] var controller = array[0].construct() // << ERROR here ``` Writing this is even worse, since the class itself does not conform to the protocol (their instances do) Type 'MyConstructor.Type' does not conform to protocol 'ControllerConstructorProtocol' ``` var array: Array<ControllerConstructorProtocol> = [ MyConstructor.self, // << ERROR here MyOtherConstructor.self, ] ``` EDIT 2016/04/23: In Swift 2.2 (Xcode 7.3) it is possible to write @rintaro's original idea :) ``` let array: Array<ControllerConstructorProtocol.Type> = [ MyConstructor.self, MyOtherConstructor.self, ] let viewController = array[0].construct() ```

Original source