How to check if Swift URL is directory

Viewed 2809

How can I check if a Swift URL represents a file or a directory?

There is a hasDirectoryPath attribute on the URL object, but there doesn't seem to be any 'intelligence' behind it. It just reflects the isDirectory value passed to the URL.

code:

  let URL1 = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
  print(URL1.hasDirectoryPath)
  let URL2 = URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)
  print(URL2.hasDirectoryPath)

output:

  false
  true
4 Answers

The name says it all "hasDirectoryPath". It doesn't state that the URL is a directory and it exists. It says that it has a directory path. To make sure that the URL is a directory you can get URL ResourceKey isDirectoryKey:

extension URL {
    var isDirectory: Bool {
       (try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
    }
}

I just simplified and upgrade what Leo answered with better return.

Info: In my answer you are getting 3 possible results, true it means the url is a directory, false it means it is not a directory and it is a file! nil means it is not a Dir or file, because we can gave a dummy url or a website to url, in this case you would return nil and a bonus information from Swift about why the return is nil and helps you solve the issue in case, to let you know what is the situation of url. With the current accepted answer a dummy url or an invalid url could be miss understood as a file for some developers.

extension URL {
    var isDirectory: Bool? {

        do {
            return (try resourceValues(forKeys: [URLResourceKey.isDirectoryKey]).isDirectory)
        }
        catch let error {
            print(error.localizedDescription)
            return nil
        }
        
    }
}

I think the misunderstanding lies in the fact that URL(fileURLWithPath:) and URL(fileURLWithPath:isDirectory:) are two different constructors, there is not a default value of false for isDirectory.

If path doesn't have a trailing slash, calling just URL(fileURLWithPath: path) will in fact check if a directory exists at the path.
We can confirm this by checking the implementation on GitHub:

public init(fileURLWithPath path: String) {
    let thePath: String = _standardizedPath(path)

    var isDir: ObjCBool = false
    if validPathSeps.contains(where: { thePath.hasSuffix(String($0)) }) {
        isDir = true
    } else {
#if !os(WASI)
            if !FileManager.default.fileExists(atPath: path, isDirectory: &isDir) {
                isDir = false
            }
#endif
    }
    super.init()
    _CFURLInitWithFileSystemPathRelativeToBase(_cfObject, thePath._cfObject, kCFURLPlatformPathStyle, isDir.boolValue, nil)
}

If path does have a "/" at the end, hasDirectoryPath will always be true.

That means you must have made a mistake because I can't reproduce the output from your question.
URL(fileURLWithPath: FileManager.default.currentDirectoryPath).hasDirectoryPath
returns true for me on macOS.


But if you want to check for certain whether there currently exists a directory at the path of a URL, you should check it like this:

var isDirectory: ObjCBool = false
if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) && isDirectory.boolValue else {
    // url is an existing directory
}

Its check directory and return true or false

extension URL    {
    func checkFileExist() -> Bool {
        let path = self.path
        if (FileManager.default.fileExists(atPath: path))   {
            print("URL AVAILABLE")
            return true
        }else        {
            print("URL NOT AVAILABLE")
            return false;
        }
    }
}

Uses:

if fileName.checkFileExist{
  // Here, do something
} 
Related