Embedded FrameworkをSwiftで作るときはpublicとかinternalとかprivateをしっかり書こう

# Embedded FrameworkをSwiftで作成

自分のアプリのWatchKit対応をしながら、そろそろコードをしっかりと共通化させようとしていたらエラーが発生してハマってしまったのでメモ。

## エラー発生

プロジェクトの構成はこのようになっています。Extensionは作成せず、Embedded FrameworkであるFrameKitと本体のアプリターゲット、SwiftFrameがあるだけです。

![PJ構成.png](https://qiita-image-store.s3.amazonaws.com/0/45525/ab0adf1a-0b55-22e6-1182-e5aa99e1cd0b.png)

FrameKitには`FrameObject`というクラスを追加しているだけです。

![エラー.png](https://qiita-image-store.s3.amazonaws.com/0/45525/d62a5823-d851-be2d-7476-1ced3f12ac44.png)

で、これを使おうとしたら *"Use of unresolved identifier `FrameObject`"* というエラーがされてコンパイルが通りませんでした。


## アクセス制御

原因はアクセス制御でした。

Swiftは *モジュール* と *ソースフファイル* をアクセス制御の単位としています。

|   |アクセスレベル|用途|
|---|---|---|
|public|どこからでもアクセス可能|どこからでもアクセス可能なのでframeworkのAPIを定義する場合はこれを使う|
|internal|デフォルトのアクセスレベル|自身のモジュールと同じモジュールのソースファイル内であればアクセス可能。何も指定しない場合はこれが指定される。|
|private|最も厳しいアクセスレベル|モジュール内の同じソースファイルでなければアクセスできない。外部に隠蔽しておきたいものはこれを指定。|

Swiftはプロジェクト内のターゲットをモジュールとみなすようです。上記の`FrameObject`はアクセスレベルを何も指定していなかったので`internal`が指定されている状態、つまりモジュール内でしか読み込めないようになっていました。

これをSwiftFrameターゲットで使用するためには`FrameObject`をモジュール外からも読み込めるようにしなくてはいけません。Objective-Cで書いているときは「公開するのだけヘッダファイルに後で書き足せばいいや」くらいの感じでやっているので気をつけないといけないですね。

ということで、`public`を指定すればエラーは起きなくなりました。

最初はクラス名に`public`をつければ問題ないかと思っていたのですが、プロパティはもちろんメソッド名にもアクセスレベルの記述をしなければいけません。


プロパティそれぞれに異なるアクセスレベルを設定してみましょう。先ほど、Embedded Frameworkに作成した`FrameObject`クラスをこんな感じに設定します。

```swift:Framekit/FrameObject.swift
public class FrameObject: NSObject {

    public   var publicName:   String
    internal var internalName: String
    private  var privateName:  String

    public init(publicName:String, internalName:String, privateName:String) {
        self.publicName   = publicName
        self.internalName = internalName
        self.privateName  = privateName
    }
}
```

![アクセスレベル.png](https://qiita-image-store.s3.amazonaws.com/0/45525/d4d59e07-ec63-48f9-540f-a543ce79d448.png)


`public`なプロパティにはアクセスできますが、`internal`、`private`にはアクセスできません。

今度は`FrameObject`を *同一モジュール内に作成* してみましょう。

```swift:SwiftFrame/FrameObject.swift
public class FrameObject: NSObject {

    public   var publicName:   String
    internal var internalName: String
    private  var privateName:  String

    public init(publicName:String, internalName:String, privateName:String) {
        self.publicName   = publicName
        self.internalName = internalName
        self.privateName  = privateName
    }
}
```

今度は`internal`にはアクセスできるようになりました。当然ですがこの場合も`private`にはアクセスできません。

![インターナル.png](https://qiita-image-store.s3.amazonaws.com/0/45525/7f616f00-e029-b50c-c5f8-ca53e3f99e28.png)


普通に考えれば当たり前ですよね。

### privateへのアクセス

上記のFrameObjectクラスを呼び出したいソースファイルの中で定義すれば`private`へのアクセスが可能になります。

![プライベート.png](https://qiita-image-store.s3.amazonaws.com/0/45525/fdff093b-4caf-a7ec-1f7e-163be62541aa.png)

*同一モジュール・同一ファイル内であればアクセスが可能*です。

### 別モジュールに同一名のクラスがあった場合

別モジュールに存在するクラスと同じクラス名が定義されていた場合はどうなるでしょう。

![モジュール.png](https://qiita-image-store.s3.amazonaws.com/0/45525/96a04639-77b7-8c8f-3205-358d47c967ca.png)

ログに注目してください。

モジュールを指定してコンストラクタを呼んだ場合は、該当するモジュールのクラスを呼び出します。

モジュールを指定しなかった場合は自身のモジュール内を探し、該当するクラスがあればそれを使用するみたいです。

## まとめ

そういえばSwiftが出たばかりのころ、Betaのころってアクセス制御なかったんですよね。全然ついていってなかった。