📁

FileManagerでディレクトリの内容を取得する

2023/11/27に公開
  • iOS/macOSでアプリケーションからファイルシステムにアクセスするFileManager
  • FileManagerでディレクトリの内容を取得(列挙)する方法とできることを紹介

1階層分だけ列挙したい場合

サブディレクトリの内容まで列挙したい場合

  • enumerator(at:includingPropertiesForKeys:options:errorHandler:)
  • このメソッド自体は列挙を行う処理ではなく、列挙を行うオブジェクト(DirectoryEnumerator)を生成するメソッド
  • contentsOfDirectoryと同様に、ファイルプロパティの取得列挙のオプションの指定ができる
  • errorHandlerは列挙の途中でエラーが発生した時に継続するかどうかを決定できるクロージャ
  • イナムレーターは列挙を行うだけではなく、列挙中にアクセスすることで、現在の階層を取得したりサブディレクトリへの再起を停止させることができる
  • 何も指定しないシンプルな例。列挙中に階層を調べて4階層目は列挙しないようにしている
    // for macOS
    do {
        let homeDirectory = FileManager.default.homeDirectoryForCurrentUser
        guard let directoryEnumerator = FileManager.default.enumerator(
            at: templatesDirectory,
            includingPropertiesForKeys: nil
        ) else { return }
        var fileURLs = [URL]()
        for case let fileURL as URL in directoryEnumerator {
            // 現在の階層
            print(directoryEnumerator.level)
            fileURLs.append(fileURL)
            // 4階層(homeDirectoryが0)目以降は列挙しない
            if directoryEnumerator.level == 3 {
                directoryEnumerator.skipDescendants()
            }
        }
        print(fileURLs)
    } catch {
        print(error)
    }
    

ファイルプロパティの取得

  • 取得したいプロパティに対応するキー(URLResourceKey)を渡すことで、URLオブジェクトに追加の情報を持たせることができる
    • キーはめっちゃ多いので紹介はしない
    • Finderで「情報を見る」で表示する内容とおおよそ同じものを取得できたりする
  • 指定したプロパティはresourceValues(forKeys:)で取得する
  • 取得したパスがディレクトリかどうか(isDirectory)を一緒に取得しフィルタする例
    do {
        let tmpDirectory = FileManager.default.temporaryDirectory
        // ディレクトリかどうかというプロパティも取得するように指定する
        let keys: Set<URLResourceKey> = [.isDirectoryKey]
        let contents = try FileManager.default.contentsOfDirectory(
           at: tmpDirectory,
           includingPropertiesForKeys: Array(keys)
        )
        // ディレクトリのものだけにフィルタ
        let directoryContents = try contents.filter { contentURL in
            let values = try contentURL.resourceValues(forKeys: keys)
            // resourceValues(forKeys:)で`.isDirectoryKey`が指定されていない場合はnilとなる
           return values.isDirectory == true
        }
        print(directoryContents)
    } catch {
        print(error)
    }
    

列挙のオプションを指定する

skipsSubdirectoryDescendants

  • サブディレクトリの内部まで列挙しないようにするオプション
  • contentsOfDirectoryでは元々この挙動なので、enumerator専用のオプション

skipsPackageDescendants

  • パッケージフォルダ(.xcodeprojファイルなど)をディレクトリではなく、ファイルとみなして子まで列挙しないようにするオプション

skipsHiddenFiles

  • .で始まる隠しファイル(フォルダ)を列挙しないようにするオプション

includesDirectoriesPostOrder

  • 列挙の順序を二分探索における帰りがけ順(postoder)で行うようにするオプション
    • 帰りがけ順の図付きの解説
  • 一括削除を行いたい場合に、末端のファイルから安全に削除したい時に有効(らしい)
  • // 指定なし
    file:///Users/me/Downloads/Sample/
    file:///Users/me/Downloads/Sample/SampleSub/
    file:///Users/me/Downloads/Sample/SampleSub/sample_sub2.txt
    file:///Users/me/Downloads/Sample/SampleSub/sample_sub1.txt
    file:///Users/me/Downloads/Sample/sample1.txt
    file:///Users/me/Downloads/Sample/sample2.txt
    
    // 指定あり
    file:///Users/me/Downloads/Sample/
    file:///Users/me/Downloads/Sample/SampleSub/
    file:///Users/me/Downloads/Sample/SampleSub/sample_sub2.txt
    file:///Users/me/Downloads/Sample/SampleSub/sample_sub1.txt
    file:///Users/me/Downloads/Sample/SampleSub/
    file:///Users/me/Downloads/Sample/sample1.txt
    file:///Users/me/Downloads/Sample/sample2.txt
    file:///Users/me/Downloads/Sample/
    

producesRelativePathURLs

  • 列挙されるURLオブジェクトを、relativePathを用いたものにするオプション
  • // 指定なし
    file:///Users/me/Downloads/
    file:///Users/me/Downloads/Sample/
    file:///Users/me/Downloads/Sample/sample.txt
    
    // 指定あり
    Downloads/ -- file:///Users/me/Downloads/
    Sample/ -- file:///Users/me/Downloads/
    Sample/sample.txt -- file:///Users/me/Downloads/
    

Discussion