Bundle
#CoreFoundation
概要
Bundle とは?
Bundle とは、広義には一定のルールに従ったディレクトリ構造、あるいはそれにアクセスするための API のこと。特に、iOS/macOS 上でソフトウェア及びそのリソースを持ち運ぶために利用される。そのため、Bundle は実行可能なアプリケーションそのものであったり、他のアプリケーションから利用される Library であったりする。
Bundle を手動で作成する必要はほぼなく、Xcode でアプリケーションを開発した場合に、自動的に必要な構造の Bundle が生成される。
システム、特に Finder は、対象が Bundle か?そうでないか?を以下の条件で判断する。
特別な拡張子を持っているかどうか
.app, .bundle, .framework, .plugin, .kext, ...
他のアプリケーションによって「パッケージである」と定義された拡張を持っているかどうか
Document Package
package bit フラグが立っているかどうか
標準化された構造を持っているため、Foundation Framework 内の Bundle class 経由で容易にアクセスできる。Bundle の構造は実際にはその種類やプラットフォームによって異なるが、それらは API レベルではカプセル化されており、開発者が気にする必要はない。
Bundle の種類
Bundle の種類には、以下のようなものがある。
App Bundle
アプリケーション自体が格納された Bundle
Framework Bundle
Framework を表すバンドル
拡張子は .framework
他の Bundle と異なり、Finder 上では通常閲覧できない
Settings Bundle
iOS において、設定情報を保持する Bundle
Abount Bundles - Bundle Programming Guide
Bundle へのアクセス
アクセスの目的
Bundle class 経由でコード上から Bundle にアクセスできる。この時アクセスする対象は、アプリケーション自身の Bundle か他の場所にある別の Bundle になる。Bundle ではないファイルシステム上の通常のディレクトリにアクセスするためには利用しない。Bundle オブジェクトを利用する基本的なパターンは以下。
指定した Bundle ディレクトリ内に Bundle オブジェクトを作成する
必要なリソースのロード/配置のために Bundle オブジェクトのメソッドを利用する
リソースとやり取りするために他のシステム API を利用する
https://developer.apple.com/documentation/foundation/bundle
Bundle を経由せずにアクセス可能なリソース
いくつかのリソースは Bundle を経由しなくともアクセスできる。例えば、Asset Catalog に登録された画像は、UIImage や NSImage のコンストラクタ経由でロードできる。同様に、.strings ファイルに格納された文字列リソースも NSLocalizedString 経由でロードできる。
目当てのBundle を探す
現在実行中のコードを保持する Bundle アプリケーションコードを含んだ Bundle を表現するのは Bundle.main。アプリケーションの場合であれば、デプロイされたアプリケーションのリソースにこれ経由でアクセスできる。注意点として、Unit test 内などから Bundle.main を参照しても、Unit test bundle ではなくテスト対象の application bundle が参照されてしまう点 がある。
code:swift
let mainBundle = Bundle.main
アプリケーションが、自身とは別の Plug-Ins, Framework や他のバンドル化されたコンテンツにアクセスしたい場合がある。これを実現するためのコンストラクタがいくつか用意されている。
init(for:)
特定クラスが所属している Bundle を取得する
Bundle へのポインタとなる情報を一々保持しておく必要がないという点で、楽に Bundle にアクセスできる
init(idenifier:)
指定した識別子をもつ Bundle を取得する
ここでの識別子には、Info.plist 内の CFBundleIdentifier に指定された値を利用する
読み出す対象の Bundle がすでにメモリ上に読み出されている、キャッシュされている場合には init(for:) よりも効率が良いが、そうでなければ効率が悪いため、init(path:)やinit(url:)を利用するのが良い
メモリ上にすでに Bundle が存在すれば、新たに生成することはせず、ロード済の Bundle のインスタンスを返す
init(url:)
URL で指定された場所にある Bundle を取得する
init(path:)
絶対パスで指定された場所にある Bundle ディレクトリからインスタンスを生成する
例えば、Framework にリンクされている場合は、その中のクラス名を指定することで Framework の Bundle にアクセスできる。
code:swift
// 特定のクラスを含んだ Bundle を取得する
let myBundle = Bundle(for: NSClassFromString("MyPrivateClass")!)
// 自身が所属する Bundle を取得する
let bundle = Bundle(for: type(of: self))
https://developer.apple.com/documentation/foundation/bundle/1417717-init
Bundleからリソースファイルにアクセスする
参考
https://developer.apple.com/documentation/foundation/bundle
Bundle Programming Guide