---
title: "Namespace your Swift extensions"
description: "SwifterSwift passed five hundred extensions and began colliding with other libraries. A member added to a type you do not own is not scoped to your library at all — it is global, and no module qualifier reaches it."
author: "Omar Albeik"
date: 2022-02-19
type: article
topics: [software, engineering]
language: en
reading_time_minutes: 5
canonical_url: https://omaralbeik.com/en/blog/protocol-oriented-extensions
translation_url: https://omaralbeik.com/ar/blog/protocol-oriented-extensions
source_url: https://omaralbeik.com/en/blog/protocol-oriented-extensions.md
---

# Namespace your Swift extensions

Extensions are the feature that made me like Swift. You can add behaviour to a
class, a struct, an enum, even a protocol, without subclassing it and without
owning its source. When I started writing Swift I liked it enough to build
[SwifterSwift](https://github.com/SwifterSwift/SwifterSwift) out of it, which
now carries more than five hundred extensions on the standard library and on
UIKit.

Somewhere past the first hundred, the pull requests started arriving with the
same problem. A useful extension is useful to more than one person, so the
better an idea is, the likelier it already ships in a library the same app
depends on.

## The collision

Two modules, both extending `Date`. One is yours; the other is something the
app already depends on and is not going to drop.

```swift
// Module A
extension Date {
  public var isToday: Bool { ... }
}

// Module B — yours
extension Date {
  public var isToday: Bool { ... }
}
```

At the call site:

```swift
Date().isToday // Ambiguous use of 'isToday'
```

The instinct is to qualify it, the way you would with two types of the same
name: `A.Date` and `B.Date` are both perfectly sayable. But a module name
qualifies a type. It does not qualify a member added to somebody else's type,
and there is no spelling of `B.isToday`, because `isToday` was never in module
B's namespace. It went into `Date`'s, next to `timeIntervalSinceNow`, and so
did the other one.

The consequence is easy to miss: a member you add to a type you do not own is
not scoped to your library at all. It is global, and it shares a namespace with
every other module in the build.

Ambiguity errors are the visible symptom. The other one appeared in SwifterSwift
years before anyone filed a bug: type a dot after a `String` in a project that
imports it, and autocomplete offers a hundred members, most of them mine, none
of them marked as such. Nothing is broken. It is just no longer possible to
tell at a glance which of these came with the language.

## A namespace of your own

If you have used RxSwift, Kingfisher or SnapKit you have already typed the
answer: `view.rx`, `imageView.kf`, `view.snp`. Each of them keeps its additions
behind a short property instead of putting them on the type.

It takes two pieces. A generic wrapper holding the value being extended:

```swift
public struct Extension<Base> {
  public let base: Base

  public init(_ base: Base) {
    self.base = base
  }
}
```

And a protocol whose only job is to hand out that wrapper:

```swift
public protocol ExtensionCompatible {}

extension ExtensionCompatible {
  public var ext: Extension<Self> { Extension(self) }
  public static var ext: Extension<Self>.Type { Extension<Self>.self }
}
```

`Self` rather than an `associatedtype`: the wrapped type is always the
conforming type, and an associated type that is only ever `Self` adds a line to
explain and nothing else.

The static overload is there so `UIColor.random` has somewhere to go. Without
it the namespace covers instance members only, the static ones stay on the bare
type, and half the library is back in the collision you just left.

Now conform the type once, and extend the wrapper instead of the type:

```swift
extension UILabel: ExtensionCompatible {}

extension Extension where Base: UILabel {
  public var trimmedText: String? {
    let text = base.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
    return text.isEmpty ? nil : text
  }
}
```

```swift
let label = UILabel()
label.text = "   hello world!  \n\n"

label.text            // "   hello world!  \n\n"
label.ext.trimmedText // "hello world!"
```

One conformance per type, and everything you add for that type afterwards goes
on the wrapper. Module A keeps its `isToday` and you keep yours, because yours
is on `Extension<Date>` and its is on `Date`. Two libraries that both do this
do not collide with each other either, as long as they chose different property
names — and `rx`, `kf` and `snp` suggest the convention holds in practice.

## What it does not cover

Mutation through the wrapper is the first trap. You will find a version of this
pattern with an empty setter on `ext`:

```swift
public var ext: Extension<Self> {
  get { Extension(self) }
  set { } // makes `foo.ext.thing = x` compile
}
```

It does make the assignment compile. For a class that is harmless: the wrapper
holds a reference, so writing through it reaches the same object. For a struct
it is worse than a compile error — the write lands on a temporary copy, the
empty setter throws it away, and the call site looks exactly like code that
works. Keep `base` a `let`, keep the wrapper read-through, and write mutations
as methods that take the value `inout` or return a new one.

Protocol conformances cannot be namespaced at all. `extension Date: Identifiable`
is a fact about `Date` for the whole program, and a second module declaring the
same conformance is a conflict no wrapper resolves. Operators are the same:
they are matched on the types of their operands and never see your namespace.
For both, the old advice stands — do not put them on types you do not own, in
code other people depend on.

And it costs four characters at every call site, forever.
`label.ext.trimmedText` is not as nice to write as `label.trimmedText`, and no
argument about namespaces makes it nicer. What you get back is that a reader
can see where the member came from, and that your additions sit together rather
than scattered through a type that already has two hundred members.

For a type you own, none of this applies; extend it directly. The pattern pays
for itself when you are adding to somebody else's type, in code somebody else
will depend on — a narrower case than it sounds, and nearly the whole of what a
library does.

SwifterSwift is the case in point, and it is too late for it. Five hundred
extensions on types nobody in that repository owns is exactly what the wrapper
is for, and adding one now would rename every member in the library at once. A
namespace cannot be introduced quietly later, so it has to be chosen before the
first release — when the library still looks too small to need one.
