Alex
Alex

Reputation: 2518

Implementing a protocol method inside of a subclass

Consider the following code inside of a Module that is included via Cocoapods (I'm using Eureka, but the problem should not be related to that):

open class FormViewController : UIViewController {  
}

extension FormViewController : UITableViewDelegate {    
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // some default implementation
  }

  /// some other UITableViewDelegate methods follow, but _NOT_ willDisplayCell
}

I am trying to subclass FormViewController and add an implementation for tableView(_:willDisplay:forRowAt:) (which is an optional method from UITableViewDelegate). This method has no implementation in the origin FormViewController:

import UIKit
import Eureka

class MyViewController: FormViewController {
    override func viewDidLoad() {
        let tableView = UITableView(frame: self.view.bounds, style: .grouped)
        tableView.delegate = self
        tableView.dataSource = self
        self.tableView = tableView
        self.view.addSubview(self.tableView!)

        super.viewDidLoad()

        form = Form()
        let section = Section()
        section.append(EmailRow(){
            $0.tag = "email"
            $0.title = "Email"
        })
        form += [section]
    }

    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        print("NOT CALLED")
    }


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("will be called")
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print("GETS CALLED")
        return super.tableView(tableView, cellForRowAt: indexPath)
    }
}

/// somewhere else:
self.navigationController?.pushViewController(MyViewController(), animated: true)

With this setup, my overriden version of tableView(_:cellForRowAt:) will be called (meaning that tableView.delegate is set up properly). tableView(_:willDisplay:forRowAt:) will not be called. As soon as I add a blank implementation in Eureka, I'm able to override the method.

Question: Why is Swift not using the method without a default implementation in the superclass?

Upvotes: 9

Views: 815

Answers (3)

Alexander Gaidukov
Alexander Gaidukov

Reputation: 759

May be the problem is in controller instance declaration.

let myController: MyFormViewController = MyFormViewController()
tableView.delegate = myController

In this case everything should work correctly. But in another case

let myController: FormViewController = MyFormViewController()
tableView.delegate = myController

tableView(_:willDisplay:forRowAt:) will not been executed.

Upvotes: -1

keithbhunter
keithbhunter

Reputation: 12334

I've noticed this problem myself, and it seems like a bug to me. You have two options.

First, you can implement the delegate method inside your extension and override it in your subclass. this will ensure the method is called.

extension FormViewController : UITableViewDelegate {    
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        // you can leave the implementation blank if you want
    }
}

class MyViewController: FormViewController {
    override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        print("CALLED!!")
    }
}

Second, you can declare the method using the pre-Swift 3 notation and it will work. This part also feels like a bug to me (or all part of the same bug). I wouldn't recommend this option as it is likely to change in future Swift or Xcode versions and generally feels hacky.

extension FormViewController : UITableViewDelegate {    
    // no willDisplayCell method
}

class MyViewController: FormViewController {
    func tableView(_ tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        print("CALLED!!")
    }
}

EDIT: The fact that OP put the UITableViewDelegate methods in an extension isn't causing the problem. The problem exists even if the class itself decalres the delegate.

Upvotes: 3

Yordi
Yordi

Reputation: 166

In my understanding extensions defined on a superclass don't work on the level of a child class if this is what you mean. You can implement a method however that you call from the child class to the super class to perform some default kind of behaviour specified in the class' extension.

Upvotes: 0

Related Questions