Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot override open method declared in class extension in another module swift

Tags:

ios

swift

swift3

I have a class extension declared in another module (using pods) like this.

public extension UIView {
  open func doStuff() {...}
}

And when I try to override this method in subclass inside my current project module

class ConcreteView : UIView {
  override open func doStuff() {...}
}

I get an error:

Overriding non-open instance method outside of its defining module

despite the method is actually marked as open

As a workaround I declared another class inside same module where extension is declared and overrided desired method there

public class CustomView: UIView {
  override open func doStuff() {...}
}

and set this class as super class for my class in main module

class ConcreteView : CustomView

so only after this I was able to override the method.

It really looks like a bug in swift3 but maybe I've omitted some understanding of why it works in that way?

like image 606
Mykola Denysyuk Avatar asked Dec 05 '16 20:12

Mykola Denysyuk


1 Answers

Short answer: The doStuff method in

public extension UIView {
  open func doStuff() {...}
}

has an effective access level "public" because the extension is marked public. Therefore it cannot be overridden in a subclass.

Note that Xcode warns

warning: declaring instance method in PUBLIC extension

and the warning text should be (see below)

warning: declaring OPEN instance method in PUBLIC extension

To solve the problem, remove the public access modifier of the extension:

extension UIView {
  open func doStuff() {...}
}

Now the extension has the access level "open" (inherited from open class UIView) and the effective access level of doStuff is "open" and it can be subclassed.

Longer answer:

Access Control in the Swift reference states that

... you can mark an extension with an explicit access-level modifier ... to set a new default access level for all members defined within the extension. This new default can still be overridden within the extension for individual type members.

but actually you can only restrict type members within the extension to the same or a lower access. Unfortunately, I could not find a definite reference for this fact in the documentation.

SE-0117 Allow distinguishing between public access and public overridability states that

For example, the true access level of a type member is computed as the minimum of the true access level of the type and the declared access level of the member. If the class is public but the member is open, the true access level is public.

but does not explain how this applies to extensions.

The check can be seen in the compiler source code TypeCheckAttr.cpp. If the access level of an item is larger then the access level of the containing extension then the diag::access_control_ext_member_more diagnostic message is emitted:

WARNING(access_control_ext_member_more,none,
    "declaring %select{PRIVATE|a fileprivate|an internal|a public}0 %1 in "
    "%select{a private|a fileprivate|an internal|PUBLIC}2 extension",
    (Accessibility, DescriptiveDeclKind, Accessibility))

Note that the "open" level is missing in the selection, and that is why it is missing in

warning: declaring instance method in PUBLIC extension

like image 81
Martin R Avatar answered Sep 18 '22 11:09

Martin R