0
votes

Let's say I have a protocol in Swift

protocol SearchCompletionProtocol {
    func searchSuccessful(results: [AnyObject])
    func searchCancelled()
}

Now let's say that I have a class and in the init method I want to pass in a single argument. The restriction is that I want this argument to be of type UIViewController and also conform to the SearchCompletionProtocol protocol. How would I go about doing that? Here are some examples of things I've tried and they all don't work.

class SearchDelegate: UISearchDisplayController, UISearchBarDelegate {

    let completionDelegate: SearchCompletionProtocol

    init<T: SearchCompletionProtocol where T: UIViewController>(completionDelegate: T) {
        self.completionDelegate = completionDelegate
        let _searchBar = UISearchBar()
        super.init(searchBar: _searchBar, contentsController: completionDelegate)
    }
}

I've also tried restricting inheritance on the protocol to only classes of type UIViewController, but that also does not work.

protocol SearchCompletionProtocol: class, UIViewController {
    func searchSuccessful(results: [AnyObject])
    func searchCancelled()
}

Of course I could easily just pass in two arguments to this method, one conforming to the search protocol and one being of type UIViewController, but that just seems not very Swifty.

2
What does your constructor look like where you call it?Alex Brown
@MartinR yes that is very close. However I would consider that a wrong answer because it's more of a hack. Yes it compiles and yes it gets you the end result, but its ugly. I'll just do two parameters before I do that.JoshA
@AlexBrown im not sure what you mean. Since I don't have the code working I don't call this constructor yet. If i took out all generics and just used two parameters it might look like SearchDelegate(completionDelegate: self, viewController: self)JoshA
In what way didn't they work if you didn't compile them?Alex Brown

2 Answers

0
votes

So it looks like the solution to this was me stripping out my original code thinking it wasn't important. Here was my original code

protocol SearchCompletionProtocol {
    func searchSuccessful(results: [RTruck])
    func searchCancelled()
}

class SearchDelegate: UISearchDisplayController, UISearchBarDelegate {
    let completionDelegate: SearchCompletionProtocol

    init<T: SearchCompletionProtocol where T: UIViewController>(completionDelegate: T) {
        self.completionDelegate = completionDelegate
        let _searchBar = UISearchBar()
        super.init(searchBar: _searchBar, contentsController: completionDelegate)
        self.completionDelegate.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Search, target: self, action: "showSearchBar")
    }
}

The issue here is that I used self.completionDelegate.navigationItem. The type of the class level completionDelegate is just the SearchCompletionProtocol and it is not of type T. removing the self caused it to use the passed in argument which was guaranteed to be a UIViewController in the eyes of the compiler and everything worked just fine. Here's the working code

protocol SearchCompletionProtocol {
    func searchSuccessful(results: [RTruck])
    func searchCancelled()
}

class SearchDelegate: UISearchDisplayController, UISearchBarDelegate {
    let completionDelegate: SearchCompletionProtocol

    init<T: SearchCompletionProtocol where T: UIViewController>(completionDelegate: T) {
        self.completionDelegate = completionDelegate
        let _searchBar = UISearchBar()
        super.init(searchBar: _searchBar, contentsController: completionDelegate)
        completionDelegate.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Search, target: self, action: "showSearchBar")
    }
}
0
votes

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

func foo(vc: UIViewController & SearchCompletionProtocol) {
    vc.searchCancelled()
}