LoginSignup
66
62

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-03-06

型名の変更に使える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にして次のように定義しました。

struct Product {
    /// 製品ID
    let id: Int32

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

    init(id: Int32) {
        self.id = id
    }
}

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

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

struct Product {
    /// 製品IDの型
    typealias ProductIdType = Int32

    /// 製品ID
    let id: ProductIdType

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

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

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

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

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

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

protocol MyProtocol {
    typealias T
}

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

protocol OtherProtocol {
    typealias U: MyProtocol // typealias(ジェネリクスの仲間)の型を制限
}

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

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

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

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

// サーバ応答をパースして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に適合した型に実装します。

// 雑誌用
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のレスポンスを受け取れるようになります。

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つ目の例は応用が利くと思いますので、知らなかった方は導入していただけると嬉しいです。

66
62
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
66
62