SwiftのExtensionをモダンでオシャレに実装する

## Extensionをモダンでオシャレにしたい

この記事の結論を先に書いておくと、

```swift
UIColor.yamabuki
```

のように書いているExtensionを以下のようにオシャレに書くことができる、ということを解説します。

```swift
UIColor.ex.yamabuki
```

長くなってんじゃねーか。

と、思うかもしれませんが、**こうすることによって型に含まれる標準的な機能と拡張機能を明確に区別することができます。**

ちょっと便利。ちょっと。

## Extensionをどうやって定義するか問題

なぜこのような話をするのかというと、Extensionをどうやって定義するかが問題になったりするからです。小さな問題ではあるんですけど。

Extensionは欲しいところに欲しいものを作ることができるので、結構使う機会が多いんじゃないかと思いますが、どこにどうやって定義するかまで考えたいところです。

局所的に使うものであれば`private`なり`fileprivate`なりのアクセスコントロールを使って公開範囲を制限したりするかと思います。

逆にプロジェクト全体で使うようなものに関しては、`public`にして`Class+Extension.swift`みたいなファイルを作成して定義するかと思います。

これらの方法でも問題ないんですが冒頭に述べたとおり、

```swift
UIColor.yamabuki
```

のような書き方だとデフォルトで定義されているものなのか、拡張定義されているものなのか分かりません。

## Extensionであることを明確にする

冒頭で書いたような、

```swift
UIColor.ex.yamabuki
```

みたいな定義を[RxSwift](https://github.com/ReactiveX/RxSwift)が行っています。

RxのようなReactive Programmingを行うための拡張の場合、このように明示的に書くことは有効なのではないかと思います。

これから実装方法の解説のためにRxSwiftのコードが出てきますが、本題とはあんまり関係ないのであまり深く考えないでサラッと読み流してください（RxSwiftやRxCocoaの違いなどの説明は省略します）。


## コードの解説

RxSwiftの2系では、ボタンがタップされたときに`tap`と出力するには以下のようなコードを書いていました。

```swift:Swift2
button.rx_tap
  .subscribeNext { _ in
    print("tap")
  }
  .addDisposable(disposeBag)
```

これがRxSwift3系では以下のように変更されています。

```swift:Swift3
button.rx.tap
  .bindNext { _ in
    print("tap")
  }
  .addDisposable(disposeBag)
```

`subscribeNext`が`bindNext`に変わっていますがほぼ同じことをしているので、そこは一旦置いておきます。

**`button.rx_tap`→`button.rx.tap`** と変わっていることに着目してみましょう。

### Rxでの内部実装

2系では[このように実装されていました](https://github.com/ReactiveX/RxSwift/blob/rxswift-2.0/RxCocoa/iOS/UIButton%2BRx.swift#L17-L25)。

```swift
extension UIButton {
    
    /**
    Reactive wrapper for `TouchUpInside` control event.
    */
    public var rx_tap: ControlEvent<Void> {
        return rx_controlEvent(.TouchUpInside)
    }
}
```

`UIButton`に限らず、Rx拡張は単純に`rx_*`というプリフィックスをつけてExtensionを定義していました（全部に目は通していませんがたぶんそうです）。

なんかオシャレじゃない感じ。

それが、3系では以下のようにジェネリクスを使って[モダンでオシャレな実装](https://github.com/ReactiveX/RxSwift/blob/3c55a309a24fbe3dbc480431798eec072b633b85/RxCocoa/iOS/UIButton%2BRx.swift#L17-L25)に変更されました。

```swift
extension Reactive where Base : UIButton {

    /**
    Reactive wrapper for `TouchUpInside` control event.
    */
    public var tap: RxCocoa.ControlEvent<Swift.Void> { get }
}
```

以上を踏まえてもう一度見てみましょう。

```swift:Swift2
button.rx_tap
  .subscribeNext { _ in
    print("tap")
  }
  .addDisposable(disposeBag)
```

```swift:Swift3
button.rx.tap
  .bindNext { _ in
    print("tap")
  }
  .addDisposable(disposeBag)
```

このあたりの変更内容については https://github.com/ReactiveX/RxSwift/blob/db75d01f3179eb6e3e534ed29815ac7920b5674b/CHANGELOG.md#features で解説されています。

Swiftの`collection`で定義されている`lazy`にインスパイアされて作られたようです。

## 実装方法

`ex`というプロパティを経由してExtension定義したものにアクセスしたい場合は[以下のようなStructとProtocolを定義](https://gist.github.com/motokiee/c2617c790f89cf6d54f2e5b907222359)します。

```swift
struct Extension<Base> {
    let base: Base
    init (_ base: Base) {
        self.base = base
    }
}

protocol ExtensionCompatible {
    associatedtype Compatible
    static var ex: Extension<Compatible>.Type { get }
    var ex: Extension<Compatible> { get }
}

extension ExtensionCompatible {
    static var ex: Extension<Self>.Type {
        return Extension<Self>.self
    }

    var ex: Extension<Self> {
        return Extension(self)
    }
}
```

Extensionであることを分かりやすくするために、`Extension<Base>`としてみました。

この`Extension<Base>`で`ExtensionCompatible`に対応した型をラップ、そして`ExtensionCompatible`のデフォルト実装で`.ex`というプロパティを定義しています。


今回は`Extension<Base>`でしたが、RxSwiftは`Reactive<Base>`としています。必要であれば用途ごとにラッパーを作ってみるのも良さそうです。

準備ができたので冒頭で見たコードを実装しましょう。

```swift
UIColor.ex.yamabuki
```

これは以下のように実装します。

```swift
extension UIColor: ExtensionCompatible {}

extension Extension where Base: UIColor {
    static var yamabuki: UIColor {
        return UIColor(red:0.97, green:0.71, blue:0.00, alpha:1.0)
    }
}
```

これでOK。`Extension<Base>`が`UIColor`だった場合のデフォルト実装として定義しています。

`UIColor`に対する拡張ではなく、`Extension<Base>`の制約付きの拡張ですね。


## まとめ

通常のExtensionでここまでするかというと、多分しないと思うのですが、RxSwiftみたいに通常のプロパティとObservableなプロパティを分けたいような場合には定義がやりやすくなる気がします。

今回取り上げたような単純なExtensionの定義でも明確に分けたい場合は結構役に立つのではないかと思います。