Declare an array of protocol compliant classes

Let's say I 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 containing classes of objects that will conform to such a protocol. How can I declare this? Ideally, I would like the compiler to check that the array is correctly filled (at compile time), and not to check (runtime) myself using the as operator.

Here is what I tried without success :(

  • This results in a compilation 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 match the protocol (their instances)

    Type 'MyConstructor.Type' does not conform to the 'ControllerConstructorProtocol' protocol

     var array: Array<ControllerConstructorProtocol> = [ MyConstructor.self, // << ERROR here MyOtherConstructor.self, ] 

EDIT 2016/04/23 . In Swift 2.2 (Xcode 7.3) you can write the original idea of โ€‹โ€‹@rintaro :)

 let array: Array<ControllerConstructorProtocol.Type> = [ MyConstructor.self, MyOtherConstructor.self, ] let viewController = array[0].construct() 
+7
swift
source share
4 answers

"an array of protocol-compliant classes" can be declared as 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 

The invocation method on the "not implemented" element.

At this point, you should declare the protocol as @objc and call the method through AnyClass . Moreover, for some reason, we cannot directly use array[0] in AnyClass , instead we must pass 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. The casting task has been fixed in Swift 1.2 / Xcode 6.3. But "unrealized" is "not applicable": (


Just random ideas:

It depends on your actual use case, but in this particular case, the closure array ()-> UIViewController? :

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

If you have several methods, you might want to use a wrapper with a protocol type.

 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() 
+8
source share

Try the following:

  var myConst = MyConstructor() var myOthConst = MyOtherConstructor() var array:[AnyObject] = [ myConst, myOthConst ] for a in array { if a is MyConstructor { println("a is type of MyConstructor") (a as! MyConstructor).myMethod() } else if a is MyOtherConstructor { println("a is type of MyOtherConstructor") (a as! MyOtherConstructor).myMethod() } } 

Another solution, although not very pretty.

0
source share

Not sure if I got your question completely, but why not:

 var array: [ControllerConstructorProtocol] = [MyConstructor(), MyOtherConstructor()] 
0
source share

If you really want only sotre classes (which are protocol compliant), you can do it like this (in Swift 3):

If you want to create a new instance with a protocol type, you need to add init () to the protocol declaration:

 protocol SomeProtocol: ConformsToOtherProtocolIfNeeded { init(...) { ... } func someFunction(...) { ... } } class Class1: SomeProtocol { init(...) { ... } func someFunction(...) { ... } } class Class2: SomeProtocol { init(...) { ... } func someFunction(...) { ... } } 

declare an array (as above):

 var someProtocols: Array<SomeProtocol.Type> = [ Class1.self, Class2.self, ] 

and if you want to use someFunction, you need to create an instance because the elements in the array are not an instance. Example:

 for sp in someProtocols { let instance = sp.init() instance.someFunction() } 

If you want to compare the type of a class, you also need to instantiate. (Thus, you cannot use an array element directly (sp).)

Examples:

 if type(of: instance) == type(of: otherInstance) { ... } if instance is SomeProtocol { ... } if instance is Class1 { ... } 
0
source share

All Articles