Swift Needs Protected Access Level
I’ve only recently started working with Swift in a serious way. Previously, I’ve played around with it in Playgrounds, or using a quick sample app. But now I’m using it for real, in a complex real application context. And, I love it!
Swift is awesome. It brings many of things I’ve been missing in Objective-C to my iOS development. I’m more productive, and I feel less friction to modeling my code more naturally.
Swift is gung-ho on evident and responsible mutability. It promotes use of the “let” keyword to define constants, and deeply enforces immutability with Optionals at compilation whenever possible. This takes some getting used to, but it is a definite strength of the language’s design.
But, Swift omits a “protected access level.” Protected access is useful for managing direct and indirect mutability in class hierarchies, and without it, we’re forced to do silly things or accept unexpected mutations.
Access Levels in Swift
When designing a class, Swift offers three access levels for methods and properties:
- Public: anything can see and interact with your method or property. These are the promises you’re making.
- Internal: only code within the same module can interact with your method or property.
- Private: only code within the same source file can interact with your method or property. A bit strange. Source files ideally are one file = one class, for better maintainability and cleaner source control interaction.
These access levels are pretty typical in object-oriented languages (except the part about “same file”).
Protected access typically means “accessible to itself and subclasses only.” Here’s how the C# documentation defines it:
A protected member of a base class is accessible in a derived class only if the access occurs through the derived class type.
The Case Against Protected Access
The Swift Blog has a post about this topic:
In contrast, protected conflates access with inheritance, adding an entirely new control axis to reason about. It doesn’t actually offer any real protection, since a subclass can always expose “protected” API through a new public method or property.
Swift provides access control along a single, easy-to-understand axis, unrelated to inheritance. We believe this model is simpler, and provides access control the way it is most often needed: to isolate implementation details to within a class or within a framework. It may be different from what you’ve used before, but we encourage you to try it out.
Thing is, where “protected” really comes into play is in designing abstract/base classes. And, those are usually consumed as their base type. The subclass isn’t going to be able to expose “protected” API, because unless the consumer is going to do Evil Things, they’re not going to know anything about which specific subtype they’re using.
One could debate endlessly the alternative designs (e.g. everything needs a protocol!) that could circumvent such issues and thus paint the decision to exclude “protected” access as a wise one.
But let’s look at what this ends up looking like in practice. For that, I’ll turn to an example that I ran into just the other day, from Apple’s own UIKit framework.
Silly Things Happen When Protected Is Omitted
UIGestureRecognizer is an abstract class. It provides base logic and UIKit scaffolding for building your own gesture recognizer subclasses.
What does a UIGestureRecognizer subclass look like? Well, here’s the first part every subclass needs, and it is here that the problem becomes obvious:
// Copyright (c) 2008–2014 Apple Inc. All rights reserved.
// the extensions in this header are to be used only by subclasses of
// code that uses UIGestureRecognizers must never call these
The UIGestureRecognizerSubclass.h header file contains a class extension that declares methods intended to be called or overridden only by subclasses of UIGestureRecognizer. Clients that merely use concrete subclasses of UIGestureRecognizer must never call these methods (except for those noted).
That’s Protected Access Level, hacked in by way of extensions. Extensions that can be used by anything, including consumers of your UIGestureRecognizer subclass, to get at things that aren’t meant to be exposed to anything other than a subclass. This is Protected Access without any of the protection.
If there’s no value in Protected Access, as the Swift blog advocates, then why bother with things like this special header file with scary warnings in the comments? Shouldn’t these just be Public methods on UIGestureRecognizer? Of course they shouldn’t.
Protected Is Good and Sometimes Best
If there’s an alternative design that maintains clarity of intent, I’ll use that. If the design meaningfully calls for protocols, then cool, problem solved. Otherwise, I guess my conceptually Protected methods and properties will actually be Public (maybe with a couple of underscores in front and a scary warning?) until better options are available.
When Swift gains Protected Access, someone on the UIKit team can delete that UIGestureRecognizerSubclass hack and build what their conceptual model actually calls for: properties and methods that only subclasses can access. Then we’ll never run into code where someone is importing those extensions and doing bad things to meet a deadline.
I look forward to marking all my underscore-prefixed public methods protected and removing the underscores. Hopefully that day will come :)