Swift extends the courtesy of an access control annotated extension to its top level members. I’m going to call this “inheritance”, but I know there’s a better name for this but I just don’t know what it is.
Consider the following:
// Base type is public public struct MyStruct {} // Here, the extension is declared public, so each top level member // "inherits" that access level. public extension MyStruct { // This is public even if it is not annotated static var firstValue: String { return "public" } // This is also public but the compiler will warn. public static var secondValue: String { return "public but warned" } // This class is also public via "inheritance" class PublicSubclass { // However, its members must be annotated. This is public public static let publicValue = "public" // This defaults to internal static let internalValue = "internal" } }
In this example, firstValue
inherits the public
access level from the MyStruct
extension. The explicit annotation for secondValue
is warned by the compiler as unnecessary. If you treat warnings as errors, that’s a problem.
Each of the static properties are accessible outside the module except for internalValue
, as even in a public class
declaration, its members do not inherit its control level:
Before I start putting some preliminary style guidance out there, I’d like to point out a few more things about this. Here’s a second example:
internal class InternalType {} extension InternalType { public static var value: String { return "value" } }
Swift compiles this code without error. It is clearly a developer-sourced issue. The intent to make the member public is fundamentally flawed. as it exceeds the type’s access control level. This issue also exists outside of extensions, where the compiler will not warn on too-high levels for direct type members:
internal class AnotherInternalType { public var value = "value" // no warning }
You’d imagine this is a place where the compiler should up its game, no? This is a point of code that is technically functional and compilable but whose specification undercuts the documenting nature of using access control. Shouldn’t the annotation be limited and warned here?
The compiler will find mismatches between the extension ACL and the type ACL:
And that’s where the problem comes in because the guidance I’m working on says: “Do not annotate extensions with access control levels except when working with trivial utilities”. Skipping extension ACL ensures that you can meaningfully and intentionally add access control to each member declared within that extension. Each access level is co-located with the declaration it decorates. This makes your code more easily audited and its access levels will be immediately apparent as to intent and implementation.
What are your thoughts? Can you think of any reasons why extensions should ever be ACL’ed in production code? And is this just a bug/language enhancement thing or is there something I’m missing. Thanks in advance for your feedback.