【Swift】エクステンション（extension）について

# 目次
[はじめに](#はじめに)
[エクステンションで可能なこと](#エクステンションで可能なこと)
[定義方法](#定義方法)
[コンピューテッドプロパティの追加](#コンピューテッドプロパティの追加)
[メソッドの追加](#メソッドの追加)
[イニシャライザの追加](#イニシャライザの追加)
[サブスクリプトの追加](#サブスクリプトの追加)
[新しいネストされた型の追加](#新しいネストされた型の追加)
[プロトコルへの適合](#プロトコルへの適合)



# はじめに
Swiftでは既に存在している型に、プロパティやメソッドやイニシャライザなどの型を構成する要素を追加できます。こうした**型の拡張のことをエクステンションと言います。今回はこのエクステンションについて説明したいと思います。

# エクステンションで可能なこと
エクステンションでは以下の6つのことが出来ます。

・メソッドの追加
・コンピューテッドプロパティの追加
・イニシャライザの追加
・サプスクリプトの追加
・新しいネストされた型の追加
・プロトコルへの適合

# 定義方法
エクステンションはextensionキーワードで宣言でき、{ }内に型を構成する要素を定義します。
```swift
extension 拡張する対象の型 {
    対象の型に追加したい要素
}
```
# メソッドの追加
エクステンションで追加したメソッドは、通常のメソッドと同様に使用できます。
次の例では、String型を拡張して、自身の値をprintするprintSelf( )というメソッドを追加しています。
```swift
extension String {
    func printSelf() {
        print(self)
    }
}

let string = "abc"
string.printSelf()  // abc
```
# コンピューテッドプロパティの追加
エクステンションでは**ストアドプロパティを追加することはできません**が、コンピューテッドプロパティは追加できます。（ここで私はなんでストアドプロパティは追加できないの？と疑問に思ったのですが、そもそもエクステンションは標準で備わっている数値型やString型の機能を拡張することが前提の目的であり、それらの標準で用意されている型には既に十分なストアドプロパティが用意されているため、エクステンションではストアドプロパティの追加は出来なくなっているのだと勝手に思っています。笑）
既存の型にコンピューテッドプロパティを追加することで、アプリケーション内で既存の型に対して頻繁に行われる処理を型自身に定義できます。
次の例では、Doubole型に各国の通貨単位を円単位に変換するコンピューテッドプロパティを追加しています。
```swift
extension Double {
    var dollar: Double { return self * 134.4 }
    var euro: Double { return self * 144.08 }
    var pound: Double { return self * 168.64 }
}

let minimumWageUS = 7.25.dollar
print("アメリカの最低賃金は\(minimumWageUS)円です")  // アメリカの最低賃金は974.4円です
let minimumWageUK = 9.50.pound
print("イギリスの最低賃金は\(minimumWageUK)円です")  // イギリスの最低賃金は1602.08円です

```
# イニシャライザの追加
エクステンションではイニシャライザを追加することもできます。既存の型にイニシャライザを追加することで、アプリケーションの固有の情報から既存の型のインスタンスを生成するということも可能となります。ただしエクステンションでは、コンビニエンスイニシャライザは追加できますが、**指定イニシャライザやデイニシャライザは追加できません**。
次の例では、アプリケーション固有のエラーを表すWebAPIError型を定義し、アラート画面を表示するUIAlertControllerという既存のクラスに、このWebAPIError型の値を引数に取るイニシャライザconvenience init(webAPIError:)を追加しています。
イニシャライinit(webAPIError:)はエラーからタイトルとメッセージを引き出し、元のイニシャライザに渡すことで、アラート画面の作成を簡略化しています。
```swift
enum WebAPIError: Error {
    case connectionError(Error)
    case fatalError

    var title: String {
        switch self {
        case .connectionError:
            return "通信エラー"
        case .fatalError:
            return "致命的エラー"
        }
    }

    var message: String {
        switch self {
        case .connectionError(let underlyingError):
            return underlyingError.localizedDescription
                + "再試行してください"
        case .fatalError:
            return "サポート窓口に連絡してください"
        }
    }
}

extension UIAlertController {
    convenience init(webAPIError: WebAPIError) {
        // UIAlertCOntrollerの指定イニシャライザ
        self.init(title: webAPIError.title,
                  message: webAPIError.message,
                  preferredStyle: .alert)
    }
}

let error = WebAPIError.fatalError
let alertController = UIAlertController(webAPIError: error)
```

# サブスクリプトの追加
エクステンションではサブスクリプトを追加することもできます。
次の例では、Int型に整数の添字を追加しています。この添字[n]は、数値の右からn位の桁を返します。
```swift
extension Int {
    subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return (self / decimalBase) % 10
    }
}
746381295[0]  // 5
746381295[1]  // 9
746381295[2]  // 2
```

# 新しいネストされた型の追加
エクステンションでは型の中に新しく型を定義することもできます。これを型のネストと言います。
次の例では、Int型に新しくKindという列挙型を追加しています。Kind型は整数が正、負、０のどれであるかを表します。また、printIntegerKindsメソッドで、Kindに応じた符号をprintさせる処理を定義しています。

```swift
extension Int {
    enum Kind {
        case negative, zero, positive
    }
    var kind: Kind {
        switch self {
        case 0:
            return .zero
        case let x where x > 0:
            return .positive
        default:
            return .negative
        }
    }
}

func printIntegerKinds(_ numbers: [Int]) {
    for number in numbers {
        switch number.kind {
        case .negative:
            print("- ", terminator: "")
        case .zero:
            print("0 ", terminator: "")
        case .positive:
            print("+ ", terminator: "")
        }
    }
    print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])  // + + - 0 - 0 + 
```

# プロトコルへの適合
エクステンショではプロトコルへの適合させることもできます。
次の例では、tableViewを使用する際に必ず使用するUITableViewDataSourceプロトコルを、エクステンションを使って適合させています。
複数のプロトコルを一つのクラスに適合させる場合やコード量が多くなる場合、このように**エクステンションでプロトコルの適合を分けてあげることで、可読性を高めることもできます**。

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! TableViewCell
        return cell
    }
}
```

# 参考
・Swift実践入門 (https://gihyo.jp/book/2020/978-4-297-11213-4)
・公式リファレンス (https://docs.swift.org/swift-book/)