16

I have recently migrated my code to Swift 4. There is an issue that I am facing with extensions, i.e.:

Declarations from extensions cannot be overridden yet

I have already read multiple posts regrading this issue. But none of them entertains the scenario described below:

class BaseCell: UITableViewCell
{
    //Some code here...
}

extension BaseCell
{
    func isValid() -> String?
    {
        //Some code here...
    }
}

class SampleCell: BaseCell
{
    //Some code here...

    override func isValid() -> String? //ERROR..!!!
    {
        //Some code here...
    }
}

According to Apple,

Extensions can add new functionality to a type, but they cannot override existing functionality.

But in the above scenario, I am not overriding the method isValid() in extension. It is overridden in the SampleCell class definition itself. Still, it is giving the error.

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
PGDev
  • 23,751
  • 6
  • 34
  • 88

6 Answers6

14

But in the above scenario, I am not overriding the method isValid() in an extension.

isValid gets declared in an extension.

The error pretty much says that if a function is declared this way, it cannot be overridden.

The statement is valid for both from an extension and in an extension.

halfer
  • 19,824
  • 17
  • 99
  • 186
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
  • Is this statement valid for both ‘from extension’ and ‘in extension’? – PGDev Oct 04 '17 at 12:31
  • Yes, it is. Overriding a function in an extension does not make much sense anyway, as that should be done by subclassing. – Tamás Sengel Oct 04 '17 at 12:32
  • Actually I have created multiple extensions of a class based on functionalities for better understanding and readability . And now I am getting this error. To resolve this I have to move all of my code to a single block. – PGDev Oct 04 '17 at 13:15
  • 2
    What is the reason behind it ? Why it is not allowed to do so ? – Prashant Tukadiya Sep 13 '18 at 13:19
7

You can override declarations from extensions as long as you @objc the protocol. In Swift 4.2:

class BaseClass {}
class SubclassOfBaseClass: BaseClass {}

@objc protocol IsValidable {
    func isValid() -> Bool
}

extension BaseClass: IsValidable {
    func isValid() -> Bool { return false }
}

extension SubclassOfBaseClass {
    override func isValid() -> Bool { return !super.isValid() }
}

BaseClass().isValid()           // -> false
SubclassOfBaseClass().isValid() // -> true
Adam Preble
  • 2,162
  • 17
  • 28
5

In Swift 3, you were able to override the function of extension if extension was of a class that is getting derived from Objective-C (http://blog.flaviocaetano.com/post/this-is-how-to-override-extension-methods/), but I guess its not possible now in Swift 4. You can ofcourse do something like this:

protocol Validity {
    func isValid() -> String?
}

class BaseCell: UITableViewCell, Validity {

}

extension Validity
{
    func isValid() -> String? {
        return "false"
    }
}

class SampleCell: BaseCell {

    func isValid() -> String? {
        return "true"
    }
}


let base = BaseCell()
base.isValid() // prints false

let sample = SampleCell()
sample.isValid() // prints true
Puneet Sharma
  • 9,369
  • 1
  • 27
  • 33
  • This is a great point, that is USED to be supported in Swift 3 but not Swift 4 anymore. Those who migrate Swift 3 projects to Swift 4 may be seeing this issue and think how their code compiled in the first place. – micnguyen Dec 19 '17 at 03:08
  • 1
    Its just that Swift 4 reduces `@objc` inference. If you declare the function `@objc` it will work as it used to. – Rafael Nobre Dec 21 '17 at 18:48
4

I think this is self-explanatory. declarations FROM extensions cannot be overridden yet

You are trying to override the function func isValid() -> String? which was declared within an extension of BaseCell, not the BaseCell class itself.

It is clearly saying that you can't override something that was declared inside an extension.

Hope it is helpful.

Tarun Tyagi
  • 9,364
  • 2
  • 17
  • 30
3

I too had a huge legacy of Swift 3 code that used this old trick to achieve what I wanted, so when I moved to Swift 4 and started getting these errors, I was somewhat distressed. Fear not, there is a solution.

This error has to do with the way Swift 4 compiles classes and the new way it treats Objective-C classes and functions. Under Swift 3, if a class is derived from NSObject, then all the variables and functions in that class would use Objective-C's dynamic naming and lookup conventions. This approach inhibited Swift's ability to optimise the code and improve code performance and size.

To overcome these penalties, in Swift 4, only variables and functions explicitly tagged with @objc get the Objective-C treatment, everything else uses standard Swift conventions: hence the error.

Armed with this knowledge, the solution to your problem is to tag the functions in the extension you wish to be overridden as @objc, then in the child classes, override the function, but remember to include the @objc tag so your code will get called at runtime.

WARNING The is a little gotcha here: if you forget to include the @objc in the override, the compiler will not complain, but your code lacks the dynamic lookup, so never gets called at runtime.

So your code should look a bit like this:

class BaseCell: UITableViewCell {
    //Some code here...
}

extension BaseCell {
    @objc func isValid() -> String? {
        //Some code here...
    }
}

class SampleCell: BaseCell {
    //Some code here...

    @objc override func isValid() -> String? {
        //Some code here...
    }
}
ThomasW
  • 16,981
  • 4
  • 79
  • 106
pbc
  • 515
  • 4
  • 11
0

It is invalid in Swift, however not in Objective-C. So, if your method signature allows it (no objc forbidden constructs), you can declare it @objc func myMethod() and override it freely in Swift.

Rafael Nobre
  • 5,062
  • 40
  • 40