【Swift】typealiasで型に対する柔軟な設計をする

型名の変更に使えるSwiftのtypealiasを使うと、型に対して柔軟な設計ができます。どういった場面で使えるかを2つご紹介したいと思います。

【追記】
この記事はSwift2.1を前提にしています。Swift2.2からProtocolのtypealiasは「associatedtype」になりそうです。
https://github.com/apple/swift-evolution/blob/master/proposals/0011-replace-typealias-associated.md

# あとからの型変更に対応できるようにする

具体型の内部で仕様する型を一箇所で管理できるため、後で仕様変更をしたい場合に便利なケースがあります。

例を考えてみます。
ある製品を表す構造体を作る時に、そのIDを整数で管理しようと思ったとします。メモリを少しでも削減するため、IDの型を`Int32`にして次のように定義しました。

```swift:
struct Product {
    /// 製品ID
    let id: Int32
    
    /// 関連する製品のID
    var relatedProductIds = [Int32]()
    
    init(id: Int32) {
        self.id = id
    }
}
```

`Int32`がたくさん見えます。
ところが後になってIDが32bitでは足りないことに気づき、`Int64`へ変更したいと思ったとします。ところが、至るところで`Int32`で宣言しているので、修正が大変になってしまいます。

そうならないよう、最初の時点で型変更のリスクを考え、あえてtypealiasでIDの型を宣言します。

```swift:
struct Product {
    /// 製品IDの型
    typealias ProductIdType = Int32
    
    /// 製品ID
    let id: ProductIdType

    /// 関連する製品のID
    var relatedProductIds = [ProductIdType]()

    init(id: ProductIdType) {
        self.id = id
    }
}
```

こうしておくことで、後で64bitにしたくなっても`ProductIdType`を`Int64`にするだけで、修正は完了です。

後で型の変更が予想されるときは、typealiasで宣言しておくと良さそうです。


# ジェネリクスを使うときに関連する型も含めてインタフェースを決める

プロトコルではtypealiasに型を指定しないまま宣言することができます。

```swift:
protocol MyProtocol {
    typealias T
}
```

ここで気をつけたいのが、このプロトコルは**変数の型指定には使えません**。この場合は、通常のプロトコルとは異なり、**ジェネリクスの制限にだけ**使えます。

```swift:
protocol OtherProtocol {
    typealias U: MyProtocol // typealias（ジェネリクスの仲間）の型を制限
}

struct MyStructure<U: MyProtocol> { // ジェネリクスの型を制限
}

// コンパイルエラーになります
// let object: MyProtocol
// let objects: [MyProtocol]
```

このようなプロトコルの利用シーンとして一番わかりやすいのは、Web APIへのアクセスと結果の受け取りだと思います。

例として、いろんな種類の本を検索するWeb APIを考えてみます。こんな感じで、Web APIを呼ぶクラスとパースを実行するプロトコルを定義しておきます。

```swift:
// サーバ応答をパースしてResponseTypeで指定した型で返すプロトコル
protocol ResponseSerializerType {
    typealias ResponseType
    func deserialize(data: NSData) -> ResponseType
}

// Web APIを呼ぶクラス
class BookApiClient {
    private let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())

    func request<Serializer: ResponseSerializerType>(
        url: NSURL,
        serializer: Serializer,
        completionHandler: (Serializer.ResponseType) -> Void)
    {
        let task = session.dataTaskWithURL(url) { (data, response, error) in
            guard let data = data else { return }
            
            let object = serializer.deserialize(data)
            completionHandler(object)
        }
        
        task.resume()
    }
}
```
ここでのポイントは、**ジェネリクスで指定した型（`Serializer`）だけでなく、関連する型（`Serializer.ResponseType`）使って関数を定義している**点です。

この構造体を使って、Web APIに対応する型とパースロジックを`ResponseSerializerType`に適合した型に実装します。

```swift:
// 雑誌用
struct Magazine {
    let id: String
    let title: String
}
struct SearchMagazinesResponseSerializer: ResponseSerializerType {
    typealias ResponseType = [Magazine]   
    func deserialize(data: NSData) -> ResponseType {
        let magazines = [Magazine]()
        // dataをパースしてmagazinesに入れていく処理
        return magazines
    }
}

// 漫画用
struct Comic {
    let id: String
    let title: String
}
struct SearchComicsResponseSerializer: ResponseSerializerType {
    typealias ResponseType = [Comic]
    func deserialize(data: NSData) -> ResponseType {
        let comics = [Comic]()
        // dataをパースしてcomicsに入れていく処理
        return comics
    }
}
```

こうすると、`ResponseSerializerType`の関連型でWeb APIのレスポンスを受け取れるようになります。

```swift:
let client = BookApiClient()

// 雑誌を検索
let magazineSerializer = SearchMagazinesResponseSerializer()
client.request(NSURL(string: "http://ysn.bookstore.com/magazines/")!, serializer: magazineSerializer) {
    print($0.dynamicType) // -> Array<Magazine>
}

// 漫画を検索
let comicSerializer = SearchComicsResponseSerializer()
client.request(NSURL(string: "http://ysn.bookstore.com/comics/")!, serializer: comicSerializer) {
    print($0.dynamicType) // -> Array<Comic>
}
```

このように、セットした`serializer`に合わせて、コールバックの`$0`の型が決まります。使いやすいですね。


# まとめ

以上少ないですが、typealiasを活用した設計をご紹介しました。
特に2つ目の例は応用が利くと思いますので、知らなかった方は導入していただけると嬉しいです。