Swift Patterns

Namespaces

Swift Patterns
1 Namespaces 07:20
2 Abstract Classes 05:55
3 Builders 11:54
Download Your Free Copy of
The Missing Manual
for Swift Development

The Guide I Wish I Had When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy

Types need to have a unique name. In Objective-C, naming collisions with other libraries and frameworks are avoided by adding a prefix to a type. That is why we use UIView instead of View and CGRect instead of Rect.

This isn't necessary in Swift thanks to modules. In Swift, namespacing is implicit. Types are implicitly scoped by the module they are defined in. Take a look at the following example.

let number: Swift.Int = 1

Int is defined in the Swift standard library. The example shows that Int can be accessed through a module with name Swift.

let number: Int = 1

This example shows that there is no need to explicitly include the name of the module in the type declaration. This isn't true if we decide to define a struct with name Int. The following example is valid, but I hope you agree that it is confusing.

import Foundation

struct Int {

    let number: Swift.Int

}

let number1: Swift.Int = 1
let number2: Int = Int(number: number1)

We define a struct with name Int. The struct defines a property with name number of type Swift.Int. number1 is of type Swift.Int while number2 is of type Int. We need to write Swift.Int to avoid a naming collision with Int. This is a scenario that you don't run into very often if you choose the names of your types carefully.

Even though modules are an important step forward, they aren't as flexible as many developers would want them to be. Swift currently doesn't offer a solution to namespace types and constants within modules.

Structs and Namespaces

Even though Swift doesn't support namespaces within modules, there are several viable solutions to this problem. The first solution uses a struct to create a namespace and it looks something like this.

import Foundation 

struct API {

    static let baseURL = "https://example.com/v1/"
    static let token = "sdfiug8186qf68qsdf18389qsh4niuy1"

}

We define a struct, API, and declare two static, constant properties. I believe this solution was first coined by Jesse Squires several years ago. It works great, it is easy to adopt, and the syntax to access the constants is intuitive and easy to read.

import Foundation

if let url = URL(string: API.baseURL) {
    ...    
}

There is one unwanted side effect, though. The API struct can be instantiated. While this isn't a problem in itself, it may confuse other developers working on the project. You could declare the initializer of the API struct privately, making it inaccessible to other parts of the project.

Declaring the initializer of the `API` struct privately makes it inaccessible to other parts of the project.

Using a struct to host a number of static members isn't the best solution and there is even a SwiftLint rule, Convenience Type, that guards against this. There is a better solution that doesn't require a workaround.

Enums and Namespaces

The only downside of using structs is that a struct can be instantiated unless the initializer is declared privately. We can avoid this unwanted side effect by using an enum without cases. I have been using this solution for years and it works like a charm. Let's update the previous example.

import Foundation 

enum API {

    static let baseURL = "https://example.com/v1/"
    static let token = "sdfiug8186qf68qsdf18389qsh4niuy1"

}

The API enum cannot be instantiated because it defines no cases. This solution is cleaner and requires less code. It is exactly what we need.

Grouping Types

Another technique I use often in larger projects makes clever use of enums and extensions. Say we are adding the ability to add videos to a watch list. We start by creating a namespace for the feature. This is as simple as defining an enum with name WatchList.

enum WatchList {}

We can namespace the types associated with the feature by extending the WatchList enum.

import UIKit

extension WatchList {

    final class ListViewController: UIViewController {
        ...
    }

}

We don't need to worry about naming collisions and the name of the type, WatchList.ListViewController, helps document the type. You can apply this technique in any project.

let listViewController = WatchList.ListViewController()

Nesting Types

In recent years, Apple started applying a similar technique to group related types. Types that are closely related to another type are defined as nested types. Take UITableViewCell.EditingStyle as an example. EditingStyle is a nested type of UITableViewCell. This is convenient and avoids long names.

UITableViewCell.EditingStyle

Apple applies a similar technique to namespace constants that are closely related to a type. Notification names are a common example. Notification names used to have long names to add context and meaning. That is no longer true. The application life cycle notification names are constant type properties of UIApplication. didEnterBackgroundNotification is a constant, class property of UIApplication. I like this pattern a lot.

UIApplication.didEnterBackgroundNotification

Naming Collisions

In Swift, types can have the same name. Earlier in this episode, we defined a struct with name Int even though the Swift standard library also defines a struct with that name. This is acceptable as long as you avoid any ambiguity. Take a look at this example.

enum Error {

    // MARK: - Cases

    case notFound
    case badRequest
    case noConnection

}

let error: Error

We define an enum with name Error. As you know, the Swift standard library defines a protocol with that name. In this example, the error constant is of type Error, not Swift.Error.

To avoid confusion, it is fine to prefix the type with the module it is defined in. In this example, the error constant is of type Swift.Error.

let error: Swift.Error

While it is recommended to avoid names that can cause confusion or ambiguity, I ignore this recommendation from time to time. Take a look at this example. We define a class with name APIClient. The class defines an enum, APIClientError, that conforms to the Error protocol.

class APIClient {

    enum APIClientError: Error {

        // MARK: - Cases

        case notFound
        case badRequest
        case noConnection

    }

}

Naming the nested enum Error triggers a compiler error because it conflicts with Swift's Error protocol.

Namespaces in Swift with Enums

It is possible to name the enum Error if we are explicit about the definition of the protocol it conforms to. We write Swift.Error instead of Error.

class APIClient {

    enum Error: Swift.Error {

        // MARK: - Cases

        case notFound
        case badRequest
        case noConnection

    }

}

You may feel that I am overcomplicating the implementation, but I don't agree. I prefer a type with name APIClient.Error over a type with name APIClient.APIClientError. I only ever use this technique for the Error protocol, but I like it a lot as it cleans up the API.

What's Next?

Even though Swift doesn't support namespaces within modules, enums solve the problem elegantly with very little overhead. I strongly recommend using enums over structs to avoid any confusion. Namespaces keep your code clean, organized, and readable.

Download Your Free Copy of
The Missing Manual
for Swift Development

The Guide I Wish I Had When I Started Out

Join 20,000+ Developers Learning About Swift Development

Download Your Free Copy
Next Episode "Abstract Classes"