はじめに
SwiftPM (Swift Package Manager)を使ったライブラリを開発するとき、
Bundle.module
、使いたくないですよね?
swift package init --type library
↑を実行してパッケージ基盤の生成直後にBundle.module
を使おうとすると、
「Bundle.module
とか知らねぇよ」と怒られてしまいます。
黙ってnil
を返してくれればいいのに、プロパティの存在が無くなってビルドエラーになるのは使いにくいと思います。
代替方法2選
1. allBundles
static var alt1Bundle: Bundle? {
for bundle in Bundle.allBundles {
if let subBundleURL = bundle.url(forResource: "", withExtension: "bundle"),
let subBundle = Bundle(url: subBundleURL) {
return subBundle
}
}
return nil
}
Bundle.allBundles
の中から探す方法です。
ループを回すのでちょっと非効率かもしれませんが、広い範囲を調べるのでBundle.module
相当が見つかる可能性は高そうです。
2. BundleFinder
static var alt2Bundle: Bundle? {
class BundleFinder {}
let bundle = Bundle(for: BundleFinder.self)
if let subBundleURL = bundle.url(forResource: "", withExtension: "bundle") {
return Bundle(url: subBundleURL)
}
return nil
}
Bundle(for:)
の引数にはクラスしか渡せないので、BundleFinder
と呼ばれる1テクニックを使います。これによってstruct
やenum
でも使えます。
知らない人が見ると「このクラスは何?」となる可能性は考えられます。
解説
処理の流れは以下の通り。
- UnitTestがあるBundle「(ライブラリ名)Tests.xctest」を取得する。
- その下にある「(ライブラリ名)_(ライブラリ名)Tests.bundle」を探す。
- そのBundleが
Bundle.module
相当です。(独自研究)
.
├── xxxxxxxTests.xctest
│ ├── xxxxxxxTests
│ ├── xxxxxxx_xxxxxxxTests.bundle ← Bundle.module
│ │ ├── Test.txt
Bundle.module生成方法
Bundle.module
を使う方法も書いておきます。
1. Package.swift
内のtestTarget
にresources
の記述を追加する。(target
でも可)
.testTarget(
name: "xxxxxxxTests",
dependencies: ["xxxxxxx"],
resources: [
.process("./Test.txt")
]
),
2. Sources配下にファイル(本例ではTest.txt)を追加する。
├── Package.swift
├── Sources
│ └── xxxxxxx
│ ├── ・・・
└── Tests
└── xxxxxxxTests
├── Test.txt
3. ビルドする。
4. Bundle.module
が使えるようになる。
おわりに
ライブラリの開発中は問題がなかったけど、ライブラリをアプリに組み込んだ状態でビルドでしたら「Type 'Bundle' has no member 'module'」が出た!という問題を筆者は経験したことがあります。
ライブラリのテスト時には使うけど、ライブラリの配布時にはパッケージに含めないファイルがあると発生しがちだ思われます。
テスト時は本物のファイルで、配布時は空ファイルにすればいいのか?などと思案していますが、簡単で効果的な方法に辿り着いていません。
良い方法があったら教えてください。
-
呼ばれてないかもしれない ↩