Swift Protocol That Returns An Object Conforming To A Protocol

November 14, 2015

I am deep in the woods converting an existing Objective-C app to Swift. This post shows how to convert an Objective-C protocol that returns an object conforming to a protocol to Swift. Pay special attention to the typealias used in the ModalViewControllerProvider (that's the key to whole thing).

Objective-C Protocol That Returns An Object Conforming To A Protocol

The app makes use of a protocol named HRLModalViewControllerProvider to "provide" a view controller at runtime bound to a given object.

@protocol HRLModalViewControllerProvider<NSObject>

- (UIViewController<HRLModalPresentationCompletion> *)viewControllerWithObject:(id)object;

@end

The returned view controller must conform to the HRLModalPresentationCompletion in order for the view controller to be modally presented and appropriately dismissed.

@protocol HRLModalPresentationCompletion<NSObject>

@property (nonatomic, copy, readwrite) void(^completionBlock)(BOOL changed);

@end

Swift Conversion

Let's start with converting the HRLModalPresentationCompletion protocol to a Swift protocol.

protocol ModalPresentationCompletion {
    var completion: ((changed: Bool) -> Void)? { get set }
}

That protocol converts pretty easily from Objective-C to Swift. Here's how to convert the HRLModalViewControllerProvider Objective-C protocol to a Swift protocol.

protocol ModalViewControllerProvider {

    typealias V: UIViewController, ModalPresentationCompletion

    func viewControllerWithObject(object: AnyObject) -> V
}

The trick is setting up a typealias. Here's how to read the typealias. > V "is a" UIViewController that conforms to the ModalPresentationCompletion protocol

Perfect! Simply type the method's return value as V.

Bonus Conversion

Let's improve the protocol by "generifying" the method's parameter.

protocol ModalViewControllerProvider {
    typealias T: AnyObject
    typealias V: UIViewController, ModalPresentationCompletion

    func viewControllerWithObject(object: T) -> V
}

Even better!

Sample Implementation

public override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    let object = self.myDataController.objectAtIndexPath(indexPath)

    var viewController = self.viewControllerProvider.viewControllerWithObject(object)
    viewController.completion = { (changed: Bool) in
        self.dismissViewControllerAnimated(true, completion: nil)
    }

    let navigationController = UINavigationController(rootViewController: viewController)
    self.presentViewController(navigationController, animated: true, completion: nil)
}

And here's an example ModalViewControllerProvider implementation:

class SensorTrackViewControllerProvider: ModalViewControllerProvider {
    func viewControllerWithObject(object: SensorTrack) -> SensorTrackViewController {
      return SensorTrackViewController(object) // assume conforms to ModalPresentationCompletion
    }
}

Summary

The conversion turns out to be pretty simple. Just remember that typealias is the key to solving the problem.

On to the next feature.