Tuesday, June 7, 2016

Swift Protocols constrained to a specific class

Update: As of Swift 4, this is supported natively. Just do this:

var controller: UIViewController & MyProtocol

That's it!

Objective-C developers learning Swift often wonder how to represent something like UIViewController<MyProtocol>. The simple answer is that there's currently no way to directly represent that construct in Swift as of Swift 2.2. However, it's possible to work around it fairly easily with just a tiny bit of extra code.

protocol KitchenHelper: class {
    // The normal protocol stuff here
    func bakeCookies()
    func eatCake()

    // The new stuff
    var asViewController: UIViewController { get }

We've added an additional constraint to the protocol; any object that implements KitchenHelper must be able to return a UIViewController as well. Presumably, any conforming view controller would want to simply return self, so lets make that easier:

extension KitchenHelper where Self: UIViewController {
    // Let's provide a default implementation for view controllers
    var asViewController: UIViewController { return self }

Now any view controller class that conforms to KitchenHelper will automatically implement asViewController as well!

class MyViewController: UIViewController, KitchenHelper {
    func bakeCookies() {
        // sugar, flour, eggs...

    func eatCake() {
        // yum

    // 'asViewController' was automatically implemented!

func test() {
    let helper: KitchenHelper = MyViewController()
    let frame = helper.asViewController.view.frame
    print("helper's view's frame:", frame)