In Swift, how can I declare a variable of a specific type that conforms to one or more protocols? In Swift, how can I declare a variable of a specific type that conforms to one or more protocols? ios ios

In Swift, how can I declare a variable of a specific type that conforms to one or more protocols?


In Swift 4 it is now possible to declare a variable that is a subclass of a type and implements one or more protocols at the same time.

var myVariable: MyClass & MyProtocol & MySecondProtocol

To do an optional variable:

var myVariable: (MyClass & MyProtocol & MySecondProtocol)?

or as the parameter of a method:

func shakeEm(controls: [UIControl & Shakeable]) {}

Apple announced this at WWDC 2017 in Session 402: Whats new in Swift

Second, I want to talk about composing classes and protocols. So, hereI've introduced this shakable protocol for a UI element that can givea little shake effect to draw attention to itself. And I've gone aheadand extended some of the UIKit classes to actually provide this shakefunctionality. And now I want to write something that seems simple. Ijust want to write a function that takes a bunch of controls that areshakable and shakes the ones that are enabled to draw attention tothem. What type can I write here in this array? It's actuallyfrustrating and tricky. So, I could try to use a UI control. But notall UI controls are shakable in this game. I could try shakable, butnot all shakables are UI controls. And there's actually no good way torepresent this in Swift 3. Swift 4 introduces the notion of composinga class with any number of protocols.


You cannot declare variable like

var object:Base,protocol<ProtocolOne,ProtocolTwo> = ...

nor declare function return type like

func someFunc() -> Base,protocol<MyProtocol,Protocol2> { ... }

You can declare as a function parameter like this, but it's basically up-casting.

func someFunc<T:Base where T:protocol<MyProtocol1,MyProtocol2>>(val:T) {    // here, `val` is guaranteed to be `Base` and conforms `MyProtocol` and `MyProtocol2`}class SubClass:BaseClass, MyProtocol1, MyProtocol2 {   //...}let val = SubClass()someFunc(val)

As of now, all you can do is like:

class CellFactory {    class func createCellForItem(item: SpecialItem) -> UITableViewCell {        return ... // any UITableViewCell subclass    }}let cell = CellFactory.createCellForItem(special)if let asProtocol = cell as? protocol<MyProtocol1,MyProtocol2> {    asProtocol.protocolMethod()    cell.cellMethod()}

With this, technically cell is identical to asProtocol.

But, as for compiler, cell has interface of UITableViewCell only, while asProtocol has only protocols interface. So, when you want to call UITableViewCell's methods, you have to use cell variable. When you want to call protocols method, use asProtocol variable.

If you are sure that cell conforms to protocols you don't have to use if let ... as? ... {}. like:

let cell = CellFactory.createCellForItem(special)let asProtocol = cell as protocol<MyProtocol1,MyProtocol2>


Unfortunately, Swift does not support object level protocol conformance. However, there is a somewhat awkward work-around that may serve your purposes.

struct VCWithSomeProtocol {    let protocol: SomeProtocol    let viewController: UIViewController    init<T: UIViewController>(vc: T) where T: SomeProtocol {        self.protocol = vc        self.viewController = vc    }}

Then, anywhere you need to do anything that UIViewController has, you would access the .viewController aspect of the struct and anything you need the protocol aspect, you would reference the .protocol.

For Instance:

class SomeClass {   let mySpecialViewController: VCWithSomeProtocol   init<T: UIViewController>(injectedViewController: T) where T: SomeProtocol {       self.mySpecialViewController = VCWithSomeProtocol(vc: injectedViewController)   }}

Now anytime you need mySpecialViewController to do anything UIViewController related, you just reference mySpecialViewController.viewController and whenever you need it to do some protocol function, you reference mySpecialViewController.protocol.

Hopefully Swift 4 will allow us to declare an object with protocols attached to it in the future. But for now, this works.

Hope this helps!