15
14

More than 5 years have passed since last update.

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

Posted at

Embedded FrameworkをSwiftで作成

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

エラー発生

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

PJ構成.png

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

エラー.png

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

アクセス制御

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

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

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

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

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

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

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

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

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

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

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

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

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

privateへのアクセス

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

プライベート.png

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

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

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

モジュール.png

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

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

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

まとめ

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

15
14
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
15
14