Declare Array of Classes That Conform to a Protocol

Declare array of classes that conform to a protocol

"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()

How to define an array of objects conforming to a protocol?

The problem is about using the generics counterpart for protocols, type aliases.
It sounds weird, but if you define a type alias, you cannot use the protocol as a type, which means you cannot declare a variable of that protocol type, a function parameter, etc. And you cannot use it as the generic object of an array.

As the error say, the only usage you can make of it is as a generic constraint (like in class Test<T:ProtocolWithAlias>).

To prove that, just remove the typealias from your protocol (note, this is just to prove, it's not a solution):

protocol MyProtocol {
var abc: Int { get }
}

and modify the rest of your sample code accordingly:

class XYZ: MyProtocol {
var abc: Int { return 32 }
}

var list = [MyProtocol]()

You'll notice that it works.

You are probably more interested in how to solve this problem. I can't think of any elegant solution, just the following 2:

  • remove the typealias from the protocol and replace T with AnyObject (ugly solution!!)
  • turn the protocol into a class (but that's not a solution that works in all cases)

but as you may argue, I don't like any of them. The only suggestion I can provide is to rethink of your design and figure out if you can use a different way (i.e. not using typealiased protocol) to achieve the same result.

Pass an array of protocol conforming objects to function

As others have said you want to have func execute(requests: [Request]) {} in this case.

The reasoning is that func execute<T: Request>(requests: [T]) {} as a generic function is saying you want a concrete known type T that conforms to the Request protocol.

The change to [Request] from [T] lets you have an array of any type that conforms to Request instead of an array of one type that conforms.

Swift Array of Class Types that Each Conform to a Protocol and are Subclasses of Common Class

You can achieve that by having SomeClass conform to MyProtocol. Therefore every subclass would also have to conform to MyProtocol.

Then your method signature would be just:

func myMethod<T>(class: T.Type) where T: SomeClass

If that is not desired then you can change the type of myMethod to not be generic. I think swift is unable to type the array to have elements of (SomeClass & MyProtocol).Type. In your case you are not really using a generic object. You do have a specific type in mind and it is (SomeClass & MyProtocol).Type. Your code would look something like this:

protocol MyProtocol: class {

}

class SomeClass: NSObject {

}

class SubClass: SomeClass, MyProtocol {

}

class SubClass2: SomeClass, MyProtocol {

}

let classArray: [(SomeClass & MyProtocol).Type] = [SubClass.self, SubClass2.self]

func myMethod(class: (SomeClass & MyProtocol).Type) {

}

classArray.forEach {
myMethod(class: $0)
}

Can't create an Array of types conforming to a Protocol in Swift

Let's say, if we could put an instance of Thing into array foos, what will happen?

protocol Foo {
associatedtype BazType
func bar(x:BazType) -> BazType
}

class Thing: Foo {
func bar(x: Int) -> Int {
return x.successor()
}
}

class AnotherThing: Foo {
func bar(x: String) -> String {
return x
}
}

var foos: [Foo] = [Thing()]

Because AnotherThing conforms to Foo too, so we can put it into foos also.

foos.append(AnotherThing())

Now we grab a foo from foos randomly.

let foo = foos[Int(arc4random_uniform(UInt32(foos.count - 1)))]

and I'm going to call method bar, can you tell me that I should send a string or an integer to bar?

foo.bar("foo") or foo.bar(1)

Swift can't.

So it can only be used as a generic constraint.

What scenario requires a protocol like this?

Example:

class MyClass<T: Foo> {
let fooThing: T?

init(fooThing: T? = nil) {
self.fooThing = fooThing
}

func myMethod() {
let thing = fooThing as? Thing // ok
thing?.bar(1) // fine

let anotherThing = fooThing as? AnotherThing // no problem
anotherThing?.bar("foo") // you can do it

// but you can't downcast it to types which doesn't conform to Foo
let string = fooThing as? String // this is an error
}
}

How to make a swift class conform to a protocol that requires a protocol-conform type for one of its properties?

You can do this with associated types.

Declare an associated type in ProtocolA:

protocol ProtocolA {
associatedtype BType: ProtocolB
var b: BType? { get } // Note the change in the type of b
}

This is saying that b is an optional of some type that conforms to ProtocolB. What type exactly? That depends on the conformers of ProtocolA.

In A's extension, you specify what BType is:

extension A: ProtocolA {
typealias BType = B
}

And you're done!

As a result of this, you won't be able to use ProtocolA as the type of a variable:

var protocolA: ProtocolA? // error

Because you don't know what b's type is.

Swift: Array property with elements conforming to a class and multiple protocols simultaneously

I just came across this old question of mine and because the Swift language evolved since accepting the partial answer I decided to post another answer which actually solves the problem I originally asked.

From version 4, Swift supports protocol composition using the & sign which can also be composed with one class type.

class BaseClass {}
protocol Protocol1 {}
protocol Protocol2 {}

class ConformingClass1: BaseClass, Protocol1, Protocol2 {}
class ConformingClass2: BaseClass, Protocol1, Protocol2 {}

// It works for a variable holding a single object.
let object: BaseClass & Protocol1 & Protocol2 = ConformingClass1()

// And it also works for a variable holding an array of objects.
let array: [BaseClass & Protocol1 & Protocol2] = [ConformingClass1(), ConformingClass2()]


Related Topics



Leave a reply



Submit