Override protocol extension from framework

I am adapting the `SwiftForms` library (https://github.com/ortuman/SwiftForms) for use in a project using Swift 2.0. The library includes various table cells preconfigured for use (switches, date pickers, check cells, steppers, etc). A change I've introduced is to use a protocol extension to make it possible to set a custom font. Here's the idea:


public protocol FormFontDefaults {
    var titleLabelFont: UIFont { get }
}

public extension FormFontDefaults {
    var titleLabelFont: UIFont { return UIFont.preferredFontForTextStyle(UIFontTextStyleBody) }
}


The base cell conforms to the protocol so that the default font set in the protocol extension is used:

public class FormBaseCell: UITableViewCell, FormFontDefaults {
    public func configure() {
        titleLabel.font = titleLabelFont
     }

}


To set a custom font the idea is to override the protocol extension as follows:

public extension FormBaseCell {
    var titleLabelFont: UIFont { return UIFont(… custom font) }
}


If I do that inside the library the override works as expected.


But, the idea is that users can link to the library and set their own override (as shown above). However it doesn't seem possible. If I include the project as a framework dependency in a project and try to override the extension, the code will compile but it is never called, making it impossible to set a custom font.


Should this technique work?

After further investigation, I've narrowed the issue down. To test, make a Playground file and add a file to the `Sources` folder with the following code:


public protocol Greeting {
  var greeting: String { get }
}
public extension Greeting {
  var greeting: String { return "Hello from protocol extension" }
}

public class GreetingClass: Greeting {
  public var message = ""
  public init() {}

  public func getMessage() {
    message = greeting
  }
}


Now inside the main Playground file try this:


// override the protocol extension
public extension GreetingClass {
  var greeting: String { return "Hello from Class override" }
}

let greet = GreetingClass()
greet.getMessage() // sets `message` var to return value of `greeting`
greet.message // "Hello from protocol extension"
greet.greeting // "Hello from Class override"


My expectation would be that both responses should be "Hello from Class override".


If the protocol is placed inside the main Playground file (instead of in the `Sources` folder) then I get the behaviour I would expect. This is rather confusing, is it a Swift bug, or intentional behaviour?

It is not possible to override functionality (like properties or methods) in extensions. This is documented in Apple's Swift book in section "Extensions" (first note in this section): Extensions can add new functionality to a type, but they cannot override existing functionality.


The consequence of this is that the `GreetingClass` extension of the Playground file does not override the existing `greeting` property, it simply masks it (i.e. the existing `greeting` property is shadowed by the `greeting` property of the class extension).

I'm not sure this is the full story, because if the code is included all together in one file, the `GreetingClass` extension does override the protocol. What I find very bewildering is that the behaviour is different if the protocol is instead defined in a file inside the `Sources` folder.

This is clearly a bug as it's violating the "language specification."

Property is defined in the protocol so GreetingClass is implementing protocol property instead of overriding existing property. I don't see anything wrong with this if using Swift 2.


I'm not sure but maybe the module where getMessage is defined does not have a visibility to the GreetingClass's greeting property declaration.

| “I'm not sure but maybe the module where getMessage is defined does not have a visibility to the GreetingClass's greeting property declaration.”


It does appear to be the case that when a function in the class uses the protocol extension internally, it's unable to see the class extension's implementation (unless it's all declared inside the same module). Clear as mud?


Here's a more succinct example that doesn't involve setting any internal variables and the protocol clearly doesn't try to override pre-existing functionality:


public protocol Greeting {
  var greeting: String { get }
}
public extension Greeting {
  var greeting: String { return "Hello from protocol extension" }
}

public class GreetingClass: Greeting {
  public init() {}

  public func getGreeting() -> String {
    return greeting
  }
}


Inside a different module:

public extension GreetingClass {
  var greeting: String { return "Hello from Class override" }
}

let greet = GreetingClass()
greet.greeting // "Hello from Class override" - as expected
greet.getGreeting() // "Hello from protocol extension" - still bound to the protocol extension

Here's a more succinct example that doesn't involve setting any internal variables and the protocol clearly doesn't try to override pre-existing functionality:


It does try to override pre-existing functionality.


Extensions are just syntactic sugar for defining freestanding methods that take a self argument. The method that is called depends on the context the compiler has on hand when the calling code is compiled.


In the file where GreetingClass is defined, the compiler knows that GreetingClass needs a gettable greeting variable in order to be Greeting-conformant. The class definition doesn't provide one, so the compiler fills it in with a call to the extension. That call behaves just like any other method. And in particular, it's added to GreetingClass's vtable.


As far as the outside world and the runtime are considered, GreetingClass really does implement greeting.

The following Twitter discussion gives some valuable information.

https://twitter.com/owensd/status/634270773000151040


The gist of it: “A module shouldn't be able to dynamically change the behavior of another module.”


At the minute attempting to do so, silently fails, so a compiler warning of some sort would be very useful. An alternative approach is to use dynamic protocol lookup (a little more verbose, but it does the trick):


if let customized = x as? CustomProtocol {
     customized.method()
} else {
     defaultImp(x)
}
Override protocol extension from framework
 
 
Q