忘備録-Swiftの拡張

趣味でIOSアプリ開発をかじっていた自分が、改めてSwiftを勉強し始めた際に、曖昧に理解していたところや知らなかったところをメモしています。いつか書き直します。

## 参考文献

この記事は以下の書籍の情報を参考にして執筆しました。

- [詳解 Swift 第5版](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3Swift-%E7%AC%AC5%E7%89%88-%E8%8D%BB%E5%8E%9F-%E5%89%9B%E5%BF%97/dp/481560407X)



##拡張の概要

あるクラス、構造体、列挙型、プロトコルに新しい機能を追加することができる。
エクステンションと呼ぶこともある

拡張定義に追加できる定義

・計算型のインスタンスプロパティ
・格納型のタイプメソッド、計算型のタイププロパティ
・インスタンスメソッド、タイプメソッド
・イニシャライザ
・添字付け
・ネスト型定義(型はその拡張定義内でのみ使用する)

追加できないのは、格納型のインスタンスプロパティ、プロパティオブザーバ。
またそのクラスや他の拡張定義内に記述されている内容を上書きすることはできない。
宣言にfinalを指定することもできない。

##既存の型に対する拡張定義

```swift
extension String {
    func toHoge() -> String{
        "hoge"
    }
}

let fuga = "fuga"
print(fuga.toHoge())        // hoge
print(fuga)        // fuga
```

##拡張定義とイニシャライザ
拡張定義にイニシャライザを定義することは可能。しかしイニシャライザの定義には注意が必要。

クラス
指定イニシャライザ、デイイニシャライザは記述できない。
簡易イニシャライザは定義できる。
構造体
本体を宣言したモジュール別ルにコンパイルされる拡張定義にイニシャライザを書くという条件付きで利用できる。
selfを利用する前あるいはイニシャライザを終える前にself.init{}の呼び出しを行わないといけない。
本体にイニシャライザの記述がなく、既定・全項目イニシャライザを使っている場合に、
拡張定義にイニシャライザを定義できるが、既定・全項目イニシャライザを使い続けることができてしまう。

##拡張定義と継承
拡張定義を追加したクラスのサブクラスにも定義は継承されるが、サブクラスで拡張定義の上書きはできない。
また、スーパクラスの定義をサブクラスの拡張定義で上書きすることはできない。

##拡張定義とプロトコル
拡張定義にプロトコルを採用することができる。
プロトコルを採用する場合、プロトコルに適応する必要がある。

```swift
class Hoge {
    let hoge = "hoge"
}
extension Hoge: CustomStringConvertible{
    var description: String {
        "extension " + self.hoge
    }
}


let hoge = Hoge()
print(hoge)        //extension hoge
```

すでに適応している場合は宣言するだけでいい。

```swift
class Hoge {
    let hoge = "hoge"
    static func == (lhs: Hoge, rhs: Hoge) -> Bool {        // Equatableの適合条件
        lhs.hoge == rhs.hoge
    }
}
extension Hoge: CustomStringConvertible, Equatable{
// 略
}
```

##プロトコル拡張

メソッド、計算型プロパティ、添字付けの実装を記述。
通常のプロトコルでする宣言、格納型のタイププロパティ、格納型のインスタンスプロパティを記述できない。
プロトコル拡張そのものにプロトコルの継承は行えない。

プロトコルを採用したデータ型はプロトコル拡張に記述されている実装を使用できる。
プロトコル拡張で定義された実装を既定定義という。

```swift
protocol ProtocolHoge {
    var hoge: String { get }
    var toArray: [String] { get }
}

extension ProtocolHoge{
    var toArray: [String] {
        let array: [String] = [hoge]
        return array
    }
}

struct Hoge: ProtocolHoge{
    let hoge = "hoge"
}

let hoge = Hoge()
print(hoge.toArray)        //["hoge"]

```

##プロトコル拡張の制約

付属型 : プロトコル
付属型 ＝ 型

```swift
protocol SimpleVector {
        associatedtype Element        //xとyの型を統一する
        var x : Element { get set }
        var y : Element { get set }
}

extension SimpleVector where Element == String{
    func toString() -> String { x + y }
}

struct VectorGrade : SimpleVector {
        var x, y: String
}

var hoge = VectorGrade(x:"A" , y:"B")
print(hoge.toString())        // AB
```

##集合型
Set＜T＞

```swift
let hoge: Set<String> = ["apple", "orange", "peach"]
for element in hoge{
    print(element)        // 実行するたびに表示順が異なる
}
```

##集合演算
和集合などの集合演算はプロトコルSetAlgebraで定義されている。
Set型はこれに適合している。

集合演算メソッド
和集合 : A.union(B) -> Set , A.formUnion(B)
差集合 : A.subtracting(B) -> Set , A.subtract(b)
積集合 : A.intersection(B) -> Set , A.formIntersection(B)
対称差集合 : A.symmetricDifference -> Set , A.formSymmetricDifference(B)

```swift
var hoge: Set<String> = ["a", "c", "d" , "e"]
let fuga = ["b", "c"]
let union = hoge.union(fuga)
print(union)        // ["a", "e", "b", "c", "d"]
hoge.subtract(fuga)
print(hoge)        // ["a", "e", "d"]
```

##プロトコルOptionSet
swiftでは何らかの情報の組合せを表すのにプロトコルOptionSetを使う。


```swift
struct RoomOption : OptionSet {
    typealias RawValue = Int
    let rawValue: Int
    static let internet = RoomOption(rawValue: 0x001)
    static let breakfast = RoomOption(rawValue: 0x002)
    static let fullbath = RoomOption(rawValue: 0x004)
    static let all = RoomOption(rawValue: 0x007)
}

var room: RoomOption = [.internet, .fullbath]
print(room.rawValue)    // 5
if !room.contains(.breakfast){
    room.insert(.breakfast)
}
print(room.rawValue)    // 7

```