1

I was hoping this would work:

protocol Foo : UITableViewDelegate{
}

extension Foo{
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        print("ROW:\(indexPath.row)")
    }
}

class MyVC : UITableViewController, Foo{
   //...
}

No luck though, the method does not get called. Am I missing something, or is this genuinely not possible?

Also, there is no compiler warning/error, the method is just not being called, which some may consider a bug.

maniek
  • 7,087
  • 2
  • 20
  • 43

1 Answers1

1

I think this is because your are inheriting from UITableViewController which already implements UITableViewDelegate.

You can easily test this:

class MyVC: UITableViewController {
  func tableView(tableView tv: UITableView, 
                 didSelectRowAtIndexPath ip: NSIndexPath) {}
}

This will result in

error: overriding declaration requires an 'override' keyword

The compiler assumes the method is already implemented in the direct inheritance chain. Hence your default method from the protocol won't be picked.

Having said that, the variant below does not work either. Adding @objc to the protocol makes my swiftc segfault ...:

import UIKit

protocol Foo : UITableViewDelegate {
  func tableView(tableView: UITableView, didSelectRowAtIndexPath: NSIndexPath)
}

extension Foo {

  func tableView(tableView: UITableView,
                 didSelectRowAtIndexPath _: NSIndexPath)
  {
    print("abcXXX")
  }

}

class ViewController: UIViewController, UITableViewDataSource, Foo {

  override func loadView() {
    self.view = UITableView(frame: CGRect())
  }

  var tableView : UITableView { return self.view as! UITableView }

  override func viewDidLoad() {
    super.viewDidLoad()

    self.tableView.delegate   = self
    self.tableView.dataSource = self
    self.tableView.registerClass(UITableViewCell.self,
                                 forCellReuseIdentifier: "MyCell1")
  }

  func tableView(tableView: UITableView, numberOfRowsInSection _: Int) -> Int {
    return 3
  }

  func tableView(tableView: UITableView, cellForRowAtIndexPath ip: NSIndexPath)
        -> UITableViewCell
  {
    let cell = tableView
                 .dequeueReusableCellWithIdentifier("MyCell1", forIndexPath: ip)
    cell.textLabel?.text = "p[\(ip.row)]"
    return cell
  }
}

Hence I assume my answer is only 50% right. Presumably protocol extensions only work in static binding contexts, which excludes ObjC protocols?

Update: This seems to be the proper answer: Protocol-Oriented Programming with UIKit. The essence:

What we CAN'T do: Provide default implementations for Objective-C protocols.

hnh
  • 13,957
  • 6
  • 30
  • 40
  • Your first point is a valid one, this wouldn't work even if the Swift compiler would not segfault :) [this](https://gist.github.com/mmank/eb46b484042689aff044) is a minimal crasher example. – maniek Feb 28 '16 at 20:36
  • I think the crash is unrelated. Both MyVC and Foo should be `@objc` implicitly anyways because their parents are. I think the key is that protocol extension methods are dispatched vtably/statically only. That is, the UITableView will call `-respondsToSelector:` on the delegate, but the extension is not visible dynamically to that. But I could be wrong. – hnh Feb 28 '16 at 20:41