Swift’s Extensions Enable Better Designs

I love Swift’s extensions. They’re much more powerful than they seem, letting you structure your code in a fluid and flexible way, eliminating boilerplate and resulting bugs.

First, extensions let you add convenience methods to classes that you find yourself needing and wishing were there:

extension UIView {
func centerInSuperview() {
center.x = superview!.bounds.width / 2
center.y = superview!.bounds.height / 2
}
}

In languages that don’t have extensions, you find yourself using global functions, and invoking inbuilt functions as

view.centerInSuperview()

and user-defined functions as

centerInSuperview(view)

Or, in Java, as:

ViewUtil.centerInSuperview()

This is inconsistent. Callers shouldn’t have to care who defined a method in order to invoke it. I end up typing the wrong syntax, and get irritated when the compiler asked me to fix it. Even once I get it right, it’s less readable, and ugly.

Another alternative is a subclass, but subclasses make sense only if you want some instances to behave differently or have different methods from others. That’s the point of subclasses. If any view can be centered in its superview, a subclass is the wrong solution, a hack. Not to mention infeasible, since a class may already have another superclass and so won’t be able to inherit from your class.

Second, when I start coding something, it’s not clear that this will end up being a convenience method. It’s often clear only in retrospect, when I finish coding up a class, and then realise that some functions don’t need access to the state of the object, at which point I extract them out into an extension.

For example, I found myself needing to transform an image to fit in given dimensions, by cropping to the target aspect ratio, and resizing. I first wrote this code in the class where I needed it, and then realised that the transform function wasn’t accessing any of the properties of the class, and so logically doesn’t belong there. So I refactored it into an extension on the image class. In retrospect, that was the right place to put it, since it performs an operation on the image, not on the object where it happened to be defined initially.

A class with five properties and ten methods has 50 potential combinations of methods accessing properties. It’s hard to know which methods’ behavior is affected by which properties, and what that state should be for the method to work correctly (preconditions). Whereas, if you extract out three of those methods into an extension, we’re now down to 35 potential combinations. The extracted out methods are much easier to reason about and use without bugs, and so is the smaller class that remains after extraction. One way of looking at it is that if that if you can refactor into an extension method with one fewer argument, it’s better encapsulated, since it doesn’t access things it doesn’t need.

Third, you can define extensions on your own classes, not just system classes. Put your extension in a different file, and it won’t have access to the private variables and methods of the main class, again making both pieces easier to understand. For example, in Noctacam, a computational photography app for the iPhone, I have a Camera class that captures photos, and an extension Camera+Video for videos, defined in separate files. Neither should need to access the private properties or functions defined in the other file, since the implementation details of how to capture a video don’t apply to a photo, and vice-versa. Extensions let me enforce this, so I don’t have to read the code to find out. This makes it easier to read and understand than it would be if I dumped both in one big file.

The traditional, and worse, solution to this problem is defining a separate Video class, but that requires having to instantiate it, maintain a reference, probably maintain a back-reference, think about reference cycles and weak references, think about what access level each of these references should have, when to pass a Camera and when to pass a Video as an argument to some function outside these classes, and so on. All this complexity goes away with extensions. Just because I want encapsulation doesn’t mean I want to deal with more objects. Classes conflate the two, and extensions let you use whatever makes sense for your code.

Fourth, extensions can add method implementations to protocols, making them much more powerful. As a simple example, I added a method isKindOfOneOf(), which takes an array of classes and tells you if the receiver is an instance of one of them. I was able to add this to NSObjectProtocol, which already has an isKindOf() method, by invoking it repeatedly for each element in the array. In traditional languages, protocols cannot have method implementations, so I’d have to add it in a class that conforms to the protocol. But then, it won’t be available in other classes that conform to the protocol, and casts can fail at runtime. Adding a method implementation to a protocol eliminates these problems.

If you’ve defined a subclass, and you then realise that it doesn’t have any properties, you can convert your class into a protocol, so that someone can doesn’t have to choose between subclassing some other class they need to subclass and reusing your code. They can have both.

Fifth, extensions let you add convenience initialisers, which is helpful for objects that require a multi-step initialisation procedure or another object to be initialised first and then passed as an argument to init.

In addition to letting you add initialisers, extensions let you add computed properties, which are just methods with a different syntax.

Sixth, you can also extent builtin types like Float, say to add an almostEq() function that returns if two Floats are the same with some tolerance for floating-point imprecision, like 0.1. In fact, because primitive types form an inheritance hierarchy, I was able to add this method in BinaryFloatingPoint, a protocol that both Float and Double implement, so that my almostEq() function is available for both types.

Seventh, you can retrofit existing types to conform to a new protocol you’ve defined. Imagine you have code that computes the extent of overlap of UIViews. You then realise that you need to reuse it for other objects that aren’t views, but have a position and size, and therefore an overlap. Say objects in a PDF. In a language like Java, you can define a protocol that captures the commonality, but you can’t retrofit an existing class like UIView to implement your protocol. The best Java lets you do is define a UIView subclass or wrapper, but that requires repetition of code and is so clunky that it may not be worth doing. In Swift, you can retrofit UIView to implement your new protocol. If your protocol methods have compatible signatures and semantics as UIView methods, you don’t need to reimplement those methods. You can fall back to the inbuilt implementations in UIView.

Eighth, you can extend a generic type like Array to define functionality that works for all arrays regardless of the element type. Or you can define a function only for a specific Array<Photo>, say one that combines the photos to create a panorama. Thinking about an operation defined not on a single instance of a type but on a collection of them is powerful for a type system.

Ninth, you can extend structs and enums as well, by adding methods:

extension CGSize {
func swapped() {
return CGSize(width: height, height: width)
}
}

In addition to just adding methods, you can make structs and enums adopt a protocol. Which will make them “inherit” any default implementations the protocol methods may have. Since structs and enums can’t inherit from any other type, extensions are the only way to give them extra functionality without reimplementing it.

In summary, extensions in Swift are much more powerful than would seem at first. They let you structure your code in natural, flexible, loosely-coupled ways, making languages without such a feature rigid and clunky by comparison.

On-demand Leader. Earlier: IIT | Google | Solopreneur | Founder | CTO | Advisor