19

I have a NSURL object. It has address of a filesystem element, it is either a file or a directory. I want to be able to tell if the NSURL is a directory or a file.

I have already tried this, which doesn"t seem to work!

NSURL * temp ....... ;// it is initialized and has a valid value 
CFURLRef xx = (CFURLRef)CFBridgingRetain(temp);
if(CFURLHasDirectoryPath(xx)) NSLog(@"was a file");
else NSLog(@"was a folder");
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
Marci-man
  • 2,113
  • 3
  • 28
  • 76

5 Answers5

31
NSNumber *isDirectory;

// this method allows us to get more information about an URL. 
// We're passing NSURLIsDirectoryKey as key because that's the info we want to know.
// Also, we pass a reference to isDirectory variable, so it can be modified to have the return value
BOOL success = [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];

// If we could read the information and it's indeed a directory
if (success && [isDirectory boolValue]) {
    NSLog(@"Congratulations, it's a directory!");
} else {
    NSLog(@"It seems it's just a file.");
}
Marcelo
  • 9,916
  • 3
  • 43
  • 52
  • thank you for the answer, it works perfectly, but, being new to Objective C (not to programming) I can't seem to follow. Could you elaborate on how it works? – Marci-man Mar 09 '14 at 02:59
  • 1
    This is fine /only/ if the file or directory exist. Otherwise you'll get an error back and make an improper assumption about what the URL /represents/ – levigroker Jan 14 '16 at 18:48
  • Even if the directory exists, a sandboxed application may not be able to reach it. So, getting a valid result from getResourceValue: depends on the url being secure. – Elise van Looij Jan 15 '21 at 13:01
11

With Swift 5, you can check if a URL path represents a directory or a regular file using one of the following macOS Playground sample codes.


#1. Using URL's hasDirectoryPath property

import Foundation

let url = URL(fileURLWithPath: "/Users/User/Desktop")
print("is directory:", url.hasDirectoryPath)

#2. Using Filemanager's attributesOfItem(atPath:) method

import Foundation

let url = URL(fileURLWithPath: "/Users/User/Desktop/File.pdf")
let attributes = try! FileManager.default.attributesOfItem(atPath: url.path)
if let type = attributes[FileAttributeKey.type] as? FileAttributeType {
    print("is file:", type == FileAttributeType.typeRegular)
}
import Foundation

let url = URL(fileURLWithPath: "/Users/User/Desktop")
let attributes = try! FileManager.default.attributesOfItem(atPath: url.path)
if let type = attributes[FileAttributeKey.type] as? FileAttributeType {
    print("is directory:", type == FileAttributeType.typeDirectory)
}

#3. Using URLResourceValues

import Foundation

let url = URL(fileURLWithPath: "/Users/User/Desktop")
if let resources = try? url.resourceValues(forKeys: [.isDirectoryKey]) {
    let isDirectory = resources.isDirectory ?? false
    print(isDirectory)
} else {
    print("No such file or directory")
}
import Foundation

let url = URL(fileURLWithPath: "/Users/User/Desktop/File.pdf")
if let resources = try? url.resourceValues(forKeys: [.isRegularFileKey]) {
    let isFile = resources.isRegularFile ?? false
    print(isFile)
} else {
    print("No such file or directory")
}

#4. Using FileManager's fileExists(atPath:isDirectory:)

import Foundation

let url = URL(fileURLWithPath: "/Users/User/Desktop")
var isDirectory: ObjCBool = false
let fileExists = FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory)
print("is directory:", fileExists && isDirectory.boolValue)
Imanou Petit
  • 89,880
  • 29
  • 256
  • 218
  • #1 and #3 were my two answers, #2 and #4 require the directory to actually exist on the filesystem (while the question and the other answers were not making this assumption). ;) – Cœur Apr 11 '19 at 08:31
  • In a sandbox application the results of all these operations depends on whether or the url is secure (i.e. trusted). If the directory that the url points at cannot be reached, there will be no folder slash. – Elise van Looij Jan 15 '21 at 12:58
5

Starting [iOS 9, macOS 10.11, tvOS 9.0, watchOS 2.0], there is hasDirectoryPath:

url.hasDirectoryPath
Demitri
  • 13,134
  • 4
  • 40
  • 41
Cœur
  • 37,241
  • 25
  • 195
  • 267
3

If you know the file URL has been standardized, then you can test for a trailing slash.

-URLByStandardizingPath will standardize a file URL including ensuring a trailing slash if the path is a directory.

Here is a test which shows -URLByStandardizingPath adding the trailing slash:

// Get a directory, any directory will do
NSURL *initialURL = [[NSBundle mainBundle] bundleURL];
NSString *initialString = [initialURL absoluteString];

// String the trailing slash off the directory
NSString *directoryString = [initialString substringToIndex:[initialString length] - 1];
NSURL *directoryURL = [NSURL URLWithString:directoryString];

XCTAssertFalse([[directoryURL absoluteString] hasSuffix:@"/"],
               @"directoryURL should not end with a slash");

XCTAssertTrue([[[directoryURL URLByStandardizingPath] absoluteString] hasSuffix:@"/"],
              @"[directoryURL URLByStandardizingPath] should end with a slash");

As you can see, [[[directoryURL URLByStandardizingPath] absoluteString] hasSuffix:@"/"] is the test.

Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117
  • 1
    Be aware, in *iOS 8*, if the URL represents a directory which does not exist on disk, URLByStandardizingPath will *strip the trailing slash* making this test invalid. If the URL represents a directory which *does* exist, iOS 8 leaves the slash. iOS 9 leaves the slash in both cases. – levigroker Feb 16 '16 at 17:09
  • Be also aware that in a sandbox application the results of URLByStandardizingPath depends on whether or the url is secure (i.e. trusted). If the directory that the url points at cannot be reached, there will be no folder slash. – Elise van Looij Jan 15 '21 at 12:57
1

Starting iOS 8, in Swift 3, there is isDirectory:

(try? url.resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory ?? false
Cœur
  • 37,241
  • 25
  • 195
  • 267