技術はメシのタネ

底辺プログラマで技術の向上を目指しているけれどやりたい事が無くて困っている

Swiftでbundle内のファイル検索

今ひとつbundleがなんなのかわかっていないのだが、ちょっと実験してみたので書く。

test.bundle
|ーhoge.xml
|_text
  |_hoge.txt

このbundle内のxmlファイルをパースしたいのだが、まずはどうやってアクセスしてみたらいいのかを調べてみた。

// test.bundleのパスは動的っぽい
if let bundlepath = NSBundle.mainBundle().pathForResource("test", ofType: "bundle") {
    let manager = NSFileManager.defaultManager()
    if let list = manager.contentsOfDirectoryAtPath(bundlepath, error: nil) as? [String] {
       // 取れたパス名配列をバラして
        for key in list {
            // パスの実体が存在するか確認
            var isDir : ObjCBool = false
            if NSFileManager.defaultManager().fileExistsAtPath(bundlepath + "/" + key, isDirectory: &isDir) {
               // ディレクトリかどうか確認して
                if isDir {
                    println("\(key) is Directory.")
                } else {
                    println("\(key) is exist.")
                }
            } else {
                println("\(key) is not exist.")
            }
        }
    }
}

NSBundle.mainBundle().pathForResourceを使ってtest.bundleのパスを取得する。
このパスには桁数の多い16進数が含まれているため、なんかこう、動的に割り当てられるものなんじゃないかと推測してみたり。

NSFileManagerクラスのcontentsOfDirectoryAtPathメソッドを利用して、test.bundleの中身を文字列配列listとして取得する。
上のソースの実行結果を見ればわかるが、残念ながらこのlist再帰的に取得されたものではないため、test.bundle直下のファイルしか格納されていない。

その上、ファイルと書いたが、実際にはファイルとディレクトリが混在している(hoge.xmltextディレクトリ)。
このことは、その先のfileExistsAtPathメソッドで確認している。
fileExistsAtPathメソッドには引数の数によって、ファイルの存在を確認するだけのものと、そのパスが示すものがファイルなのかディレクトリなのかの判断もしてくれるものとの違いがある。

この手法の一番エレガントじゃないなと思うポイントは、test.bundleのパス文字列に/を足して、さらにtest.bundle下の格納物のパスを足してフルパス文字列を作成している点だ。

そして、それをクリアする方法もちゃんと用意されている。

if let bundlepath = NSBundle.mainBundle().pathForResource("test", ofType: "bundle") {
    // test.bundleの中身を知る
    let manager = NSFileManager.defaultManager()
    // 再帰的にファイルの一覧を取得する
    if let paths = manager.enumeratorAtPath(bundlepath) {
        while let file = paths.nextObject() as? String {
            println(file)
        }
    }
}

これを実行すると、
 hoge.xml
 text
 text/hoge.txt
この様に出力される。

textディレクトリが少し邪魔な気がするが、先のfileExistsAtPathメソッドを利用すれば、無視することも可能だろう。
/を文字列連結することなくtest.bundleの中身を知ることが出来た。

基本的にはネットでかき集めた情報の継ぎ接ぎなので、同じ目的のことをもっと詳しく知りたいという方は、「Swift 関数名」や「iOS 関数名」などで検索すると捗ると思われる。

以下、余談。
この先の作業に必要なので、アプリのサンドボックスについて調べてみた。

s31o3.hatenablog.com

なるほどなーと思ったのだが、一点だけ。

上記では
NSSearchPathForDirectoriesInDomains(directory: NSSearchPathDirectory, domainMask: NSSearchPathDomainMask, expandTilde: Bool)
ではなく
NSFileManager.defaultManager().URLsForDirectory(directory: NSSearchPathDirectory, inDomains: NSSearchPathDomainMask)
をアップルが推奨している、と書いてある。
が、iOS8.0より前のバージョンがDeployment Targetの場合、`NSSearchPathForDirectoriesInDomainsを使用すると特に問題ないのだが、URLsForDirectory使った場合、実行時にエラーが出た。

特にiOS8からはいろいろ変わったみたいなので、気をつけないといけない。

上記は取り消し。
あとで追記記事出す。