4

Using Swift 4, I'm trying to write a custom protocol which provides conformance to an @objc protocol.

Some Code

More specifically, I have a custom protocol Searchable which provides a simple implementation of the UIKit protocol UISearchBarDelegate, requiring to only implement a single callback for handling search filter changes.

@objc protocol Searchable: class, UISearchBarDelegate {
  @objc func searchFilterChanged(_: String?)
}

I provide the delegate implementation in a protocol extension:

extension Searchable {
  func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    searchFilterChanged(searchText)
  }
  // .. and some other delegate methods
}

Finally, in another extension I create and install the search bar (installSearchBar has to be called from viewDidLoad of the concrete view controller):

extension Searchable where Self: UITableViewController {
  func installSearchBar() {
    let searchBar = UISearchBar()
    searchBar.delegate = self
    tableView?.tableHeaderView = searchBar
  }
}

The Problem

Unfortunately, the delegate methods of UISearchBarDelegate are not called when the user enters text in the search bar. The compiler warns me with this message:

Non-'@objc' method 'searchBar(_:textDidChange:)' does not satisfy optional requirement of '@objc' protocol 'UISearchBarDelegate'

However, I cannot add @objc to the method because this causes the following compiler error:

@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes

The Question

So now my question is: can the desired behavior be implemented in Swift at all, or is it not possible to implement conformance to an @objc protocol in another protocol?

Note 1: I know that this can be implemented using subclass; still I'm looking for a POP version.

Note 2: I know there is UISearchController which also helps to simplify implementing search; I cannot use this class because in my actual code, I have a more complex filter view with other controls -- that's why I use a bare UISearchBar.

dr_barto
  • 5,723
  • 3
  • 26
  • 47

1 Answers1

6

This is due to the fact that Obj-C runtime uses Dynamic dispatch (dynamic lookup/message passing) to conforming object to know if it confirms the required method and invoke it.

Swift protocol extension by default use static dispatch. In a sense, when UISearchBarDelegate passes a message to your class that conforms to this protocol it can't find the extension method as this is static to the protocol. And hence your implementation is neither detected by the runtime nor does it work.

This is a very deep topic. I don't expect you to be clear with my simple paragraph. However, I tried this when Swift Protocol Extension was new (trying to generalize UITableViewDelegate and DataSource) but all in vain. I had to research into why and I can tell you from my experience and research this has to do with Objc messaging passing relying on dynamic dispatch and protocol extension being statically dispatched.

kandelvijaya
  • 1,545
  • 12
  • 20