26

I'm learning swift and wondering what's the best way to list all files with absolute path in a directory including ones from subfolders(files only)

I tried with following, but it seems listing all contents names even folder names without full paths.

let paths = FileManager.default.subpaths(atPath: folderPath)
    for p in paths! {
       print p
    }
}

as well as

let items = try fm.contentsOfDirectory(atPath: folderPath)

googling didn't come out with any working methods.

this is for macOS 10.14

any help is appreciated!

thanks

2 Answers 2

60

FileManager has also a method for a deep search: enumerator(at:includingPropertiesForKeys:options:errorHandler:)

To get only the files you have to iterate the enumerator and filter the files

let url = URL(fileURLWithPath: "/path/to/directory")
var files = [URL]()
if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) {
    for case let fileURL as URL in enumerator {
        do {
            let fileAttributes = try fileURL.resourceValues(forKeys:[.isRegularFileKey])
            if fileAttributes.isRegularFile! {
                files.append(fileURL)
            }
        } catch { print(error, fileURL) }
    }
    print(files)
}

It's highly recommended to use URLs rather than string paths.

Sign up to request clarification or add additional context in comments.

8 Comments

got var files = [URL]() Cannot invoke value of function type with argument list '()'
ok yes, it works in playground, I changed URL to NSURL, then it works, thanks a lot
Don't use NSURL in Swift. Do you have a custom object URL in your project? If yes rename it.
i don't have a custom object URL, but using URL gives ambiguous in context error somehow
var files = [String]() and files.append(fileURL.path)
|
8

FileManager is right object

If you are using swift 5 or above could take advantage of AsyncStream. Below a simple Playground

import Foundation
import Combine

// Recursive iteration     
func walkDirectory(at url: URL, options: FileManager.DirectoryEnumerationOptions ) -> AsyncStream<URL> {
    AsyncStream { continuation in
        Task {
            let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: nil, options: options)
                
            while let fileURL = enumerator?.nextObject() as? URL {
                if fileURL.hasDirectoryPath {
                    for await item in walkDirectory(at: fileURL, options: options) {
                        continuation.yield(item)
                    }
                } else {
                    continuation.yield( fileURL )
                }
            }
            continuation.finish()
        }
    }
}


// use it     
let path = URL( string: "<your path>" )

let options: FileManager.DirectoryEnumerationOptions = [.skipsHiddenFiles, .skipsPackageDescendants]
    
Task {
        
    let swiftFiles = walkDirectory(at: path!, options: options).filter {
        $0.pathExtension == "swift"
    }

    for await item in swiftFiles {
        print(item.lastPathComponent)
    }
        
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.