[swift-evolution] private(call) and internal(call)

Charles Srstka cocoadev at charlessoft.com
Mon Jan 11 18:53:57 CST 2016


The Cocoa frameworks have a long-standing issue regarding methods that are intended as primitives for subclasses to override or for protocol implementers to provide, but which aren’t meant for client code to call directly. Often, the only way to determine that a method shouldn’t be called is to look at the documentation, but that isn’t much of a help when auto-complete suggests such methods, which sometimes look perfectly usable, even tempting, to the naked eye. NSView has a number of them that have come up many a time on mailing lists and support forums in the past, to the extent that Apple has actually created a chart intended to indicate which should be called directly and which shouldn’t:

https://developer.apple.com/library/mac/releasenotes/UserExperience/RNAutomaticLayout/#//apple_ref/doc/uid/TP40010631-CH1-SW14 <https://developer.apple.com/library/mac/releasenotes/UserExperience/RNAutomaticLayout/#//apple_ref/doc/uid/TP40010631-CH1-SW14>

Clearly advertising things that are not supposed to be actually called is non-ideal in terms of interface.

Proposed Solution:

Following the example of private(set), I propose a new access modifier for methods and properties called private(call). A method declared private(call) would behave exactly the same as a private method in almost all circumstances, the one exception being when a subclass attempts to override it (for a class) or a class, struct, or enum implements it (for a protocol). These methods would not be callable by any code outside of the source file that declared the property, and would not show up in code completion.

A similar access modifier, internal(call), would allow the method to be called only by code in the same module, but publicly would be override/implement only.

Wait, isn’t this the same as protected?

No. The difference between this and protected is that while protected allows subclasses to access the method and/or property as if it were public, private(call) does *not*. Subclass code is forbidden from accessing the member, exactly as any code from any other class would be. This makes implementation simpler than it would be for protected; private(call) can be treated exactly the same as private for all use cases other than:

1. Displaying the generated “headers” to show the class or protocol’s interface.

2. A method or property declaration in a source code file that either begins with “override” or implements a required member of a protocol.

In all other cases, the compiler can treat private(call) and internal(call) exactly as it would private and internal respectively. It is also more secure than protected, since it cannot be worked around simply by making a subclass of the class in question, and since it reduces the ways a subclass can do something unexpected with the property or method.

Alternatives Considered:

In some cases, an exception could be made for Objective-C methods marked OBJC_REQUIRES_SUPER; in such cases, the subclass could be allowed to call super’s implementation, but only from within the override. This could be extended to pure-Swift classes once Swift acquires an equivalent to OBJC_REQUIRES_SUPER. Other than this one exception, even subclasses would not be able to call a method marked private(call). This would, however, complicate the implementation somewhat, and may not be necessary, since any work that the superclass needs to do could be done in a separate method, before calling the private(call) method. There may be some cases where the subclass wants to begin with the return value from the superclass method and modify it slightly, however, in which case this variant may be helpful. It could also possibly be useful in cases where there is a chain of subclasses going several layers down, where each subclass wants to do the work of its own superclass’s implementation before continuing.


All in all, I think this would be a positive contribution to the language. What do you think?

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160111/611afe14/attachment.html>

More information about the swift-evolution mailing list