Class conforming to protocol as function parameter in Swift

Swift

Swift Problem Overview


In Objective-C, it's possible to specify a class conforming to a protocol as a method parameter. For example, I could have a method that only allows a UIViewController that conforms to UITableViewDataSource:

- (void)foo:(UIViewController<UITableViewDataSource> *)vc;

I can't find a way to do this in Swift (perhaps it's not possible yet). You can specify multiple protocols using func foo(obj: protocol<P1, P2>), but how do you require that the object is of a particular class as well?

Swift Solutions


Solution 1 - Swift

You can define foo as a generic function and use type constraints to require both a class and a protocol.

Swift 4

func foo<T: UIViewController & UITableViewDataSource>(vc: T) {
    .....
}

Swift 3 (works for Swift 4 also)

func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource { 
    ....
}

Swift 2

func foo<T: UIViewController where T: UITableViewDataSource>(vc: T) {
    // access UIViewController property
    let view = vc.view
    // call UITableViewDataSource method
    let sections = vc.numberOfSectionsInTableView?(tableView)
}

Solution 2 - Swift

In Swift 4 you can achieve this with the new & sign:

let vc: UIViewController & UITableViewDataSource

Solution 3 - Swift

The Swift book documentation suggests that you use type constraints with a where clause:

func someFunction<C1: SomeClass where C1:SomeProtocol>(inParam: C1) {}

This guarantees that "inParam" is of type "SomeClass" with a condition that it also adheres to "SomeProtocol". You even have the power to specify multiple where clauses delimited by a comma:

func itemsMatch<C1: SomeProtocol, C2: SomeProtocol where C1.ItemType == C2.ItemType,    C1.ItemType: SomeOtherProtocol>(foo: C1, bar: C2) -> Bool { return true }

Solution 4 - Swift

With Swift 3, you can do the following:

func foo(_ dataSource: UITableViewDataSource) {
    self.tableView.dataSource = dataSource
}

func foo(_ delegateAndDataSource: UITableViewDelegate & UITableViewDataSource) { 
    //Whatever
}

Solution 5 - Swift

Swift 5:

func foo(vc: UIViewController & UITableViewDataSource) {
    ...
}

So essentially Jeroen's answer above.

Solution 6 - Swift

What about this way?:

protocol MyProtocol {
    func getTableViewDataSource() -> UITableViewDataSource
    func getViewController() -> UIViewController
}

class MyVC : UIViewController, UITableViewDataSource, MyProtocol {

    // ...
    
    func getTableViewDataSource() -> UITableViewDataSource {
        return self
    }
    
    func getViewController() -> UIViewController {
        return self
    }
}

func foo(_ vc:MyProtocol) {
    vc.getTableViewDataSource() // working with UITableViewDataSource stuff
    vc.getViewController() // working with UIViewController stuff
}

Solution 7 - Swift

Update for Swift 5:

func yourFun<V: YourClass>(controller: V) where V: YourProtocol

Solution 8 - Swift

Note in September 2015: This was an observation in the early days of Swift.

It seems to be impossible. Apple has this annoyance in some of their APIs as well. Here is one example from a newly introduced class in iOS 8 (as of beta 5):

UIInputViewController's textDocumentProxy property:

Defined in Objective-C as follows:

@property(nonatomic, readonly) NSObject<UITextDocumentProxy> *textDocumentProxy;

and in Swift:

var textDocumentProxy: NSObject! { get }

Link to Apple' documentation: https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIInputViewController_Class/index.html#//apple_ref/occ/instp/UIInputViewController/textDocumentProxy

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMartin GordonView Question on Stackoverflow
Solution 1 - SwiftNate CookView Answer on Stackoverflow
Solution 2 - SwiftJeroen BakkerView Answer on Stackoverflow
Solution 3 - SwiftJony TView Answer on Stackoverflow
Solution 4 - SwiftKalzemView Answer on Stackoverflow
Solution 5 - SwiftwilltherussianView Answer on Stackoverflow
Solution 6 - SwiftMuHAOSView Answer on Stackoverflow
Solution 7 - SwiftZigii WongView Answer on Stackoverflow
Solution 8 - SwiftKlaasView Answer on Stackoverflow