102

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?

1
  • You could make a custom class, for example MyViewControllerClass, and make sure that class conforms to the protocol you care about. Then declare the argument accepts that custom class. I realize it wouldn't work for every situation but, it's a way... not an answer to your question though. More of a workaround. Commented Aug 19, 2016 at 19:45

8 Answers 8

140

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)
}
Sign up to request clarification or add additional context in comments.

7 Comments

I think it's a little unfortunate that this is required. Hopefully in the future there will be a cleaner syntax for this, like protocol<> provides (but protocol<> can't contain non-protocol types).
This makes me sooooo sad.
Just out of curiosity, can't you explicitly unwrap numberOfSectionsInTableView because it's a required function in UITableViewDataSource?
numberOfSectionsInTableView: is optional—you might be thinking of tableView:numberOfRowsInSection:.
In Swift 3 this appears to be deprecated as of Xcode 8 beta 6 with a preference for: func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource { ... }
|
33

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

let vc: UIViewController & UITableViewDataSource

Comments

17

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 }

1 Comment

Link to the documentation would have been nice to see.
5

Swift 5:

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

So essentially Jeroen's answer above.

Comments

4

With Swift 3, you can do the following:

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

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

1 Comment

This only applies to protocols, not protocol & class in swift 3.
2

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
}

Comments

1

Update for Swift 5:

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

Comments

0

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

5 Comments

This seems auto-generated: Swift protocols can be passed around as objects. Theoretically they could just type var textDocumentProxy: UITextDocumentProxy! { get }
@atlex2 You've lost the NSObject class type in favor of the UITextDocumentProxy protocol type.
@titaniumdecoy No you are wrong; you do still have NSObject if UITextDocumentProxy is declared like most protocols are: @protocol MyAwesomeCallbacks <NSObject>
@CommaToast Not in Swift, which is what this question is about.
@titaniumdecoy Yeah, you were right originally. I was confused! Sorry to say you were wrong. On the upside you do still have NSObjectProtocol... in this instance... but I know it's not the same thing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.