[Swift 3.0] 新しく追加されたGeneric Type Aliasesについて

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

おばんです、通っている武術教室の打ち合い練習で先生の基本攻撃力が高くて、良いところに入ったわけでもないのに痛みにもだえる田中です。「基礎防御力!人生の基本でしょう!?」、と田舎の女の子に怒られる声が聞こえるので筋トレします。

今日はSwift3のtypealiasにジェネリクスが使えるようになる話です。
参照元は以下です。(以降このリンク先を「公式」と差してブログを書いていきます)

typealiasにジェネリクスが使えるようになるとは

公式のMotivationに書いてあることとしては以下があります。

Generic typealiases are an obvious generalization of the existing Swift model for type aliases, which allow you to provide a name for an existing nominal generic type, or to provide a name for a non-nominal type (e.g. tuples, functions, etc) with generic parameters.

Generic Type Aliasesが存在することで、タプルや関数のような名前付けすることのできない型をジェネリクスな引数とともに宣言することができるようになる。

typealiasにジェネリクスが使えると何が良いのか

以下のような表現ができるようになります。

typealias StringDictionary<T> = Dictionary<String, T>
typealias DictionaryOfStrings<T : Hashable> = Dictionary<T, String>
typealias IntFunction<T> = (T) -> Int
typealias Vec3<T> = (T, T, T)
typealias BackwardTriple<T1,T2,T3> = (T3, T2, T1)

このtypealiaseの宣言時の型引数に型をセットする書き方ははSwiftのジェネリクスに対するアプローチに沿った書き方だとのこと。

既存のコードへの影響

全く新しい機能なので既存のコードへの影響は全く無い。

思いつく使い方

たとえば通信処理などの非同期処理におけるcompletionHandlerを定義するのが楽になると考えました。
以下がその例です。

typealias CompletionHandler<T> = ((T?, NSError?) -> ())
// ジェネリクスにStringを指定した例
func getName(completionHandler: CompletionHandler<String>) {
    completionHandler("Mitsuha", NSError(domain: "男子の視線!スカート注意!人生の基本でしょう?", code: 999, userInfo: nil))
}

getName { name, error in
    // Mitsuha: 「Error Domain=男子の視線!スカート注意!人生の基本でしょう!? Code=999 "(null)"」
    print("\(name): 「\(error)」")
}
// 複数の返却値を持ちたくてジェネリクスにデータモデルを指定した例
struct Godzilla {
    let version: Int
    let position: String
}

func getGodzilla(completionHandler: CompletionHandler<Godzilla>) {
    completionHandler(Godzilla(version: 1, location: "蒲田"), nil)
}

getGodzilla { godzilla, error in
    // Godzilla(version: 1, location: "蒲田")
    print(godzilla)
}

一つのオプショナルのクラスと一つのオプショナルのNSErrorを返すCompletionHandler型。
たとえばこれが出来ないSwift 3より前ではcompletionHandlerの型をいちいち書かなくてはいけないのですが、ジェネリクスで指定することができると「必ず一つのクラスと一つのNSErrorを返す」という定義のもとでは記述がとても楽になるように思います。
なので、返却値が複数ある場合はデータモデルにまとめて返すとすれば通信処理なんかとは相性が良いはずです。

// Swift 3より前
func getHoge(completionHandler: (hoge: Hoge?, error: NSError?)) {}
// Swift 3以降
func getHoge(completionHandler: CompletionHandler<Hoge>) {}

まとめ

completionHandlerをいちいち書くのが辛く、「必ずNSErrorは含むけど、その他のパラメータの型は可変だしどうしようかなああああ...」と思っていたので個人的にありがたいです。
特に最近はクリーンアーキテクチャで書いてみたりをしているのですが、PresenterとUseCaseをまたぐ処理の時にcompletionHandlerをとても頻繁に宣言するので今後取り入れていこうと思います。

参考