Four Clever Uses of Swift Extensions

If you're watching this, then I assume you're familiar with Swift extensions. A Swift extension allows you to add functionality to a type, a class, a struct, an enum, or a protocol. But extensions are more powerful than that. In this episode, I'd like to show you four clever uses of Swift extensions.

Protocol Conformance

The Swift Programming Language mentions that extensions can be used to conform an existing type to a protocol. While this isn't new or revolutionary, it can also help you keep your code organized.

Take the UITableViewDataSource and UITableViewDelegate protocols as an example. This example may look familiar. This is fine, but it results in a lengthy class implementation that can become difficult to navigate over time.

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    ...

}

You can keep your code organized by creating an extension for each protocol the type conforms to.

import UIKit

class ViewController: UIViewController {

    ...

}

extension ViewController: UITableViewDataSource {

    ...

}

extension ViewController: UITableViewDelegate {

    ...

}

Navigating source files also becomes easier if you make it a habit to use the jump bar at the top of Xcode's source editor.

Navigating source files also becomes easier if you make it a habit to use the jump bar at the top of Xcode's source editor.

Preserving Initializers

I learned the next trick from Chris Eidhof. For this example, we first need to define a structure, Person. The structure defines two constant properties of type String, first and last.

struct Person {

    // MARK: - Properties

    let first: String
    let last: String

}

Swift generously creates an initializer for us, init(first:last:), which we can use to instantiate an instance of the Person structure. This isn't new.

let john = Person(first: "John", last: "Doe")

Unfortunately, the initializer is no longer available if we define a custom initializer in the struct's definition.

struct Person {

    // MARK: - Properties

    let first: String
    let last: String

    // MARK: - Initialization

    init(dictionary: [String: String]) {
        self.first = dictionary["first"] ?? "John"
        self.last = dictionary["last"] ?? "Doe"
    }

}

The initializer is no longer available if we define a custom initializer in the structure definition.

Fortunately, we have an easy workaround to resolve this issue. We create an extension for the Person struct in which we define the custom initializer.

struct Person {

    // MARK: - Properties

    let first: String
    let last: String

}

extension Person {

    // MARK: - Initialization

    init(dictionary: [String: String]) {
        self.first = dictionary["first"] ?? "John"
        self.last = dictionary["last"] ?? "Doe"
    }

}

An extension solves the problem.

Code Separation

We can take the previous example one step further. A few years ago, Natasha Murashev outlined a technique that uses extensions to separate state from behavior. If we apply this technique to the previous example, we end up with something like this.

struct Person {

    // MARK: - Properties

    let first: String
    let last: String

}

extension Person {

    // MARK: - Initialization

    init(dictionary: [String: String]) {
        self.first = dictionary["first"] ?? "John"
        self.last = dictionary["last"] ?? "Doe"
    }

    // MARK: - Public API

    var asDictionary: [String: String] {
        return [ "first": first,
                 "last": last ]
    }

}

The type definition only defines the stored properties. An extension is created for the behavior of the type, that is, methods and computed properties. The result is a clear separation of state (stored properties) and behavior (methods and computed properties).

We can take this one step further by creating a second private extension for private behavior.

struct Person {

    // MARK: - Properties

    let first: String
    let last: String

}

extension Person {

    ...

}

private extension Person {

    ...

}

Code separation and organization is very easy to do using extensions. I use it all the time. If you miss Objective-C's header files, then this is a nice alternative.

Nested Types

The Swift Programming Language mentions that extensions also allow you to define and use nested types. But I feel this feature is undervalued. I use it in every Swift project, for example, to define constants.

A few months ago, I published a tutorial about building a custom control using a bitmask. In that tutorial, we store the raw value of the bitmask in the user defaults database.

// MARK: - Actions

@IBAction func scheduleDidChange(_ sender: SchedulePicker) {
    // Helpers
    let userDefaults = UserDefaults.standard

    // Store Value
    let scheduleRawValue = sender.schedule.rawValue
    userDefaults.set(scheduleRawValue, forKey: UserDefaults.Keys.schedule)
}

Instead of using a string literal, we use a constant. We create an extension for the UserDefaults class in which we define an enum without cases, Keys. The enum defines one static constant property of type String, schedule.

extension UserDefaults {

    enum Keys {

        static let schedule = "schedule"

    }

}

The result is quite nice if you ask me. Not only can we group constants, avoiding literals that are scattered throughout the codebase, we also namespace the constants. In other words, the constants are easy to remember and make sense.

UserDefaults.Keys.schedule

With the release of Swift 3, Apple adopted a similar technique in some of its frameworks.

Notification.Name.UIApplicationWillTerminate

What's Next

Extensions are quite powerful in Swift and the techniques I showed in this tutorial are only a few examples of what's possible.

Stop Writing Swift That Sucks

Download the Swift Patterns I Swear By