【iOS】【Objective-C】【Swift】指定したディレクトリにあるファイルの一覧を取得する方法

# はじめに
Swiftで、ドキュメントの一覧を出す方法ってなんかあるか調べてみました。

# 指定したディレクトリにあるファイルの一覧を取得する方法

　iOSのiOSのSwiftで指定したディレクトリにあるファイルの一覧を取得する方法については、以下のように簡潔にまとめることができます。

~~~Swift:指定したディレクトリにあるファイルの一覧を取得する方法 例
if let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
    do {
        let fileURLs = try FileManager.default.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil)
        // <#取得したファイル一覧を利用する処理を記述#>
    } catch {
        print("Error while enumerating files \(documentsURL.path): \(error.localizedDescription)")
    }
}
~~~

　このコードでは、FileManagerクラスのcontentsOfDirectoryメソッドを使用して、指定されたディレクトリに含まれるファイル一覧を取得しています。このメソッドは、指定されたディレクトリ内の浅い検索を行い、含まれている項目のURLの配列を返します。

　例えば、上記のコードでは、.documentDirectory（iOSアプリのドキュメントディレクトリ）を指定しています。このディレクトリに含まれるファイル一覧が取得され、fileURLsに代入されます。取得したファイル一覧を利用する処理は、コメントで記述されています。

## try-catch文 による例外処理の対応
上記のコードでは、try-catch文を使用して、エラーハンドリングを行っています。tryブロック内でエラーが発生した場合には、catchブロックでエラーを受け取り、適切な処理を行います。

~~~Swift:指定したディレクトリにあるファイルの一覧を取得する方法 例 catch の抜粋
} catch {
    print("Error while enumerating files \(documentsURL.path): \(error.localizedDescription)")
}
~~~

エラーを受け取るために、catchブロックの引数にError型のerrorという名前を設定しています。このerrorは、tryブロックで発生したエラーの情報を持っています。ここでは、エラーが発生した場合には、エラーメッセージを出力するようにしています。

例外処理を推奨しているので、何らかのエラーが発生した場合にはエラーメッセージが出力しておくようにしておくとエラーが発生した時に何のエラーが発生しているかがわかります。

こちらのコードは、Swift 4以降で使用可能です。また、深い列挙を行う場合には、enumeratorメソッドを使用する必要があります。


### enumeratorメソッド

enumeratorメソッドは、FileManagerクラスに定義されているメソッドで、指定されたディレクトリ以下に含まれるファイルやディレクトリを再帰的に取得することができます。contentsOfDirectoryメソッドと異なり、深い列挙を行うことができるため、サブディレクトリ内に含まれるファイルやディレクトリも取得することができます。

enumeratorメソッドは、URLを引数に取り、URLの配列を返します。以下は、enumeratorメソッドを使用して、指定されたディレクトリ以下のすべてのファイルとディレクトリを取得する例です。

~~~Swift:Swift 4でenumeratorメソッドを使用して、指定されたディレクトリ以下のすべてのファイルとディレクトリを取得する例
if let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
    let fileManager = FileManager.default
    let keys = [URLResourceKey.isDirectoryKey, URLResourceKey.localizedNameKey]
    let enumerator = fileManager.enumerator(at: documentDirectory, includingPropertiesForKeys: keys)

    while let url = enumerator?.nextObject() as? URL {
        do {
            let resourceValues = try url.resourceValues(forKeys: Set(keys))
            if resourceValues.isDirectory == true {
                print("\(url.path) is a directory")
            } else {
                print("\(url.path) is a file")
            }
        } catch {
            print("Error: \(error.localizedDescription)")
        }
    }
}
~~~

　上記のコードでは、documentDirectoryに指定されたディレクトリ以下に含まれるすべてのファイルとディレクトリを取得しています。enumeratorメソッドにより、ファイルのURLの配列を取得し、whileループで順次アクセスしています。取得したURLに対して、resourceValuesメソッドを使用して、URLの情報を取得しています。この情報には、isDirectoryKeyやlocalizedNameKeyなどが含まれており、ディレクトリであるかどうかなどを判断するために使用されます。

　enumeratorメソッドは、contentsOfDirectoryメソッドと異なり、深い列挙を行うことができるため、サブディレクトリ内に含まれるファイルやディレクトリも取得することができます。ただし、再帰的にアクセスするため、パフォーマンスに影響がある場合がありますので、必要最小限の範囲で使用するようにしてください。

上記のenumeratorメソッドを使用した例は、Swift 4以降での書き方です。Swift 3以前では、enumeratorメソッドは使用されておらず、代わりにenumerator(at:includingPropertiesForKeys:options:errorHandler:)メソッドが使用されていました。

# Swift3
以下は、Swift 3でenumeratorメソッドを使用して、指定されたディレクトリ以下のすべてのファイルとディレクトリを取得する例です。

~~~Swift:Swift 3でenumeratorメソッドを使用して、指定されたディレクトリ以下のすべてのファイルとディレクトリを取得する例
if let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
    let fileManager = FileManager.default
    let keys = [URLResourceKey.isDirectoryKey, URLResourceKey.localizedNameKey]
    if let enumerator = fileManager.enumerator(atPath: documentDirectory) {
        while let url = enumerator.nextObject() as? URL {
            do {
                let resourceValues = try url.resourceValues(forKeys: Set(keys))
                if resourceValues.isDirectory == true {
                    print("\(url.path) is a directory")
                } else {
                    print("\(url.path) is a file")
                }
            } catch {
                print("Error: \(error.localizedDescription)")
            }
        }
    }
}

~~~
上記のコードでは、enumeratorメソッドを使用する代わりに、enumerator(at:includingPropertiesForKeys:options:errorHandler:)メソッドを使用しています。このメソッドも、深い列挙を行うことができます。enumeratorメソッドと同様に、URLResourceKeyを使用して、ディレクトリであるかどうかを判断しています。

ただし、Swift 3以前でenumeratorメソッドを使用することはできませんので、ご注意ください。

Swift 2, 1でファイルの一覧を取得する場合は、NSFileManagerクラスのcontentsOfDirectoryAtPathメソッドを使用します。

以下は、Swift 2での例です。

# Swift2

~~~Swift:Swift 2でファイルの一覧を取得する場合は、NSFileManagerクラスのcontentsOfDirectoryAtPathメソッドを使用例
let fileManager = NSFileManager.defaultManager()
if let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first {
    let filePaths = try? fileManager.contentsOfDirectoryAtPath(documentDirectory)
    if let paths = filePaths {
        for path in paths {
            print(path)
        }
    }
}
~~~
　上記のコードでは、NSFileManagerクラスのdefaultManagerメソッドを使用してファイルマネージャのインスタンスを取得し、contentsOfDirectoryAtPathメソッドで指定されたディレクトリのファイル一覧を取得しています。取得したファイルパスは、for-inループを使用して順番に出力されます。

# Swift1 
Swift 1では、NSSearchPathForDirectoriesInDomainsメソッドの引数の指定が異なるため、以下のようなコードになります。

~~~Swift:Swift 1でファイルの一覧を表示する例
let fileManager = NSFileManager.defaultManager()
if let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as String? {
    let filePaths = fileManager.contentsOfDirectoryAtPath(documentDirectory)
    if let paths = filePaths {
        for path in paths {
            print(path)
        }
    }
}
~~~

　Swift 1やSwift 2では、NSFileManagerクラスのcontentsOfDirectoryAtPathメソッドを使用することで、ファイルの一覧を取得することができます。ただし、Swift 3以降で推奨されるenumeratorメソッドを使用することで、より柔軟なファイル操作が可能になりますので、可能であればenumeratorメソッドを使用することをお勧めします。

### ObjectiveC についても同じようにファイル一覧を出す方法

　Objective-Cでファイルの一覧を取得する場合は、NSFileManagerクラスのcontentsOfDirectoryAtPath:error:メソッドを使用します。

以下は、Objective-Cでの例です。

~~~Objective-C:Objective-Cでファイルの一覧を表示する例
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSError *error;
NSArray *filePaths = [fileManager contentsOfDirectoryAtPath:documentDirectory error:&error];
if (filePaths) {
    for (NSString *filePath in filePaths) {
        NSLog(@"%@", filePath);
    }
}　else {
    NSLog(@"Error: %@", error);
}
~~~
上記のコードでは、NSFileManagerクラスのdefaultManagerメソッドを使用してファイルマネージャのインスタンスを取得し、contentsOfDirectoryAtPath:error:メソッドで指定されたディレクトリのファイル一覧を取得しています。取得したファイルパスは、for-inループを使用して順番にログに出力されます。

ファイルの一覧を取得するときには、エラーの発生を考慮する必要があります。contentsOfDirectoryAtPath:error:メソッドはエラーが発生した場合にはnilを返します。上記の例では、NSErrorオブジェクトを使用してエラーを取得しています。エラーが発生した場合は、必要に応じてエラーをログに出力したり、エラーハンドリングを行うようにしてください。

上記の例では、NSErrorオブジェクトを使用してエラーを取得しています。NSErrorオブジェクトがnilでない場合には、エラーの内容をログに出力しています。

NSErrorオブジェクトには、エラーが発生した原因に関する情報が含まれています。NSErrorオブジェクトのdomainプロパティとcodeプロパティを使用することで、エラーの原因を特定することができます。詳細については、公式ドキュメントを参照してください。

# まとめ
割と長くなりましたが、Swiftのバージョンによって使われていないものもあるので、バージョンによる書き方をアップデートしてみてください。またSwiftだけじゃなくて現場によってはObjective-Cで書かれている現場もあったりするので焦らず調べてみてください。

---

## 関連記事
- [【iOS】【Objective-C】リファレンス - Swift,Objective-C まとめ](https://qiita.com/sunstripe2011/items/b08a9d7956998893fe65)
- [サンストライプについて](http://qiita.com/sunstripe)　- About

---

### 制作チーム：サンストライプ
[![sunstripe_logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/80600/322f2d60-f19d-af9e-2b6f-7290ca546848.png)](http://sunstripe.main.jp/)
http://sunstripe.main.jp/
[サンストライプについて](http://sunstripe.main.jp/)
制作チームのサンストライプのコンテンツについては以下の通り

月3回のコンテンツをリリースしています。
参加者は、プログラマーやデザイナー、イラストレーター、声優やVTuberなどが募集中です。

#### 関連プロジェクト・支援団体

- [地域情報 THEメディア](https://the.themedia.jp/)
地域活性化をテーマに様々な情報を発信しています。

- [ワークショップ情報 THEワークショップ](https://workshop.themedia.jp/)
多様化の時代に向けて他者理解を鍛えることを目的としています。

- [青空プログラミング](https://workshop.themedia.jp/posts/7057553?fbclid=IwAR32CfRQ7RvDOhvuogoSwqxDLMOU7Jvuzljlnx-TMc27qBPX1BnWsfLcBR8)　　- 月１だけのプログラミング学習塾
プログラミングワークショップ・ウェブ塾の開講！！！
様々なテーマでプログラミングに取り組むことができます。


- [プログラミングラボ](https://twitter.com/PLab2020) - オンラインプログラミング研究チーム
３ヶ月にコンテンツを作成するAIを活用した実践形式の開発

- [Vの輪 バーチャル促進イベント企画・開発](https://twitter.com/VTuberLinks) - メタバースサークル
メタバースに向けた企画やイベントの実施およびメタバースとリアルの融合を目指してます。

- [スクランブルスクール バーチャル学校](https://twitter.com/scramblevschool) - バーチャル学校
VTuberに向けた情報発信とバーチャルイベントの企画と実施およびバーチャル番組制作

#### その他協力応援 / 支援者の集い
- [トラストヒューマン](http://trusthuman.co.jp/) - 私たちは何よりも信頼､人と考えてます｡
人材戦略パートナーとして、コンサルティングやクリエイティブの両角度からサポートをしています。
- キャリア教育事業 - [広域学習支援プラットフォーム『のびのび日和』](https://slc-lab.amebaownd.com/)