開発メモ

開発関係のメモをいろいろと。たぶん。

Swiftにおける名前空間の問題点と、その回避方法

動作を確認した環境

環境 情報
Xcode 8.0 (8A218a)
Swift 3.0
Date 2016/10/12

Swiftにおける名前空間とは?

モジュールの名前のことです。

そもそも『モジュールとは何か?』と言う話を始めると長くて面倒な割りにメリットが少ないので省略しますが、簡単に言うと『import ○○○』の形で指定する、『○○○』がモジュールの名前になります。

複数のモジュールで同じ識別子が使われている場合、名前空間を使うことで、どのモジュールの識別子を使うか指定することが出来ます。

名前空間を指定して識別子を指定する例:

import UIKit

// 両方のモジュールに『JunkClass』が存在する
import JunkFramework1
import JunkFramework2

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // これはコンパイルエラー
        JunkClass.printName()

        // 名前空間を指定すれば問題ない
        JunkFramework1.JunkClass.printName()
        JunkFramework2.JunkClass.printName()
    }
}

Swiftにおける名前空間の問題点

名前空間は便利ですが、別モジュールへのextensionを区別できないという問題点があります。

例えば、複数のモジュールでこんなextensionを定義した場合。

extension String {
    public static func extensionName() {
        // フレームワークの名前を出力
        print("JunkFramework1 - extension")
    }
}

現在のSwiftでは、どのモジュールで定義されたextensionを使うか、指定することが出来ません。

import UIKit

// 両方のモジュールにStringへのextensionが存在する
import JunkFramework1
import JunkFramework2

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // 識別子がぶつかっているのでコンパイルエラー
        String.extensionName()

        // 以下、すべてコンパイルエラー → モジュールを指定できない
        JunkFramework1.String.extensionName()
        String.JunkFramework1.extensionName()
        (JunkFramework1.String).extensionName()
        (String.JunkFramework1).extensionName()
    }
}

Swift 3でどうにかなるかと思ってましたが・・・ 無理でした。このままだと、Swift 4でもこの問題は残ったままになりそうです。

名前空間の問題点を回避する方法

では、どうやってこの問題を回避するべきでしょうか?

1. extensionを作る時点で、prefixを付けておく

モジュールを作る側が、名前がぶつからないようにしておく方法です。

『prefixが付いてるなんてSwiftらしくない』と言う意見が出そうですが、『どのモジュールで定義された物かすぐにわかる』といったメリットもあります。たぶん。

2. extensionを使う側で、どれか1つだけ使うようにする

モジュールを使う側で工夫する方法です。

基本的に、よく使うモジュールだけでをimportするようにして、そうでないモジュールは呼び出し元を1カ所にまとめて、何らかの形でラップします。

そもそも、extensionの名前がぶつかる事なんて滅多にないと考えれば、これはこれで有りかもしれません。

3. その他

いつか、Swiftがこの問題をどうにかしてくれるのを待ちましょうか・・・