Creating Paths and Locating Directories

This article describes how to create URLs and strings that represent paths, and how to locate standard directories in the filesystem.

Creating Paths

NSString provides a number of path-utility methods that you can use to, for example, extract the components of a path (the directory, filename, and extension), create paths from those components, “translate” path separators, clean up paths containing symbolic links and redundant slashes, and perform similar tasks.

In Mac OS X v10.6 and later, NSURL provides a number of path-utility methods analogous to those provided by NSString. Where possible, you should typically use URL-based methods rather than string-based methods, because the URL-based operations are usually much more efficient than the string-based equivalents. Rather than manipulating paths as strings, therefore, where possible you should use NSURL objects directly.

Whether you use NSString or NSURL, whenever you need to perform any operation on a path, you should use these methods rather than any other approach.

URL-Based Path Utilities

In Mac OS X v10.6 and later, you should typically use NSURL-based APIs to perform file-related operations. The following code fragment shows how, given a URL that contains a file path, you can determine the filename and path extension, remove the filename to determine the directory that contains the file, and create a new file URL in which the original file name is prepended by “Copy of”. (For example, if the original path were /Users/me/MyFile.txt, the new path would be /Users/me/Copy of MyFile.txt.)

NSURL *url = <#URL containing a file path#>;
 
NSString *extension = [url pathExtension];
NSString *fileName = [[url lastPathComponent] stringByDeletingPathExtension];
NSString *copyFileName = [@"Copy of " stringByAppendingString:fileName];
 
NSURL *copyURL = [url URLByDeletingLastPathComponent];
copyURL = [copyURL URLByAppendingPathComponent:copyFileName];
copyURL = [copyURL URLByAppendingPathExtension:extension];

(To create a copy of a file in the same way that Finder does, see Moving Files to the Trash.)

String-Based Path Utilities

The following code fragment shows how, given a string that contains a file path, you can determine the filename and path extension, remove the filename to determine the directory that contains the file, and create a new path in which the original file name is prepended by “Copy of”. (For example, if the original path were /Users/me/MyFile.txt, the new path would be /Users/me/Copy of MyFile.txt.)

NSString *path = <#String containing a file path#>;
 
NSString *extension = [path pathExtension];
NSString *fileName = [[path lastPathComponent] stringByDeletingPathExtension];
NSString *copyFileName = [@"Copy of " stringByAppendingString:fileName];
 
NSString *copyPath = [path stringByDeletingLastPathComponent];
copyPath = [copyPath stringByAppendingPathComponent:copyFileName];
copyPath = [copyPath stringByAppendingPathExtension:extension];

(To create a copy of a file in the same way that Finder does, see Duplicating Files.)

Standard System Directories

Mac OS X and iOS define a number of standard directories—for example directories to contain a user’s documents or system frameworks. Obviously the location of some of these directories—such as the user’s documents directory—is not fixed (it depends on the current user), however, in general you should not rely on other directories, such as the system frameworks directory, remaining in the same location across different versions of the operating system. Rather than hard-coding directory paths, therefore you should use one of the functions or methods that help you to locate standard directories in the file system.

Cocoa provides the following functions that return path strings for a few standard directories directly:

You can use the returned values in conjunction with NSString path utility methods to create other paths, for example:

NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"MyFile.txt"];

For other standard directories, you use the NSSearchPathForDirectoriesInDomains function (which returns paths as NSString objects) or—in Mac OS X v10.6 and later—the URLsForDirectory:inDomains: and URLForDirectory:inDomain:appropriateForURL:create:error: methods of NSFileManager (which return URLs). The function and the methods use constants to identify the directory—or directories—you’re interested in. There are two types of constant:

In general, NSSearchPathForDirectoriesInDomains and URLsForDirectory:inDomains: may return multiple values if the parameters imply multiple locations. However, you should not make any assumptions as to the number of paths returned. Some domains or locations might be made obsolete over time, or other new domains or locations might be added (while preserving the older ones); in either case, the number of paths in the returned array might increase or decrease. Simply look at all of the returned values if you want to enumerate all of the files, or, if you just want to copy or move a file to a location and multiple paths are returned, use the first one in the array.

Locating Directories as URLs

In Mac OS X v10.6 and later, you can use an NSFileManager object to locate standard system directories. Its methods return NSURL objects rather than string-based paths.

URLsForDirectory:inDomains: is analogous to NSSearchPathForDirectoriesInDomains. You specify the type of directory and the domain you want to find and the method returns an array of URLs. The following example illustrates how you can use the method to get a URL for the current user’s Documents directory:

NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *urls = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
if ([paths count] > 0) {
    NURL *userDocumentsURL = [urls objectAtIndex:0];
    // Implementation continues...

URLsForDirectory:inDomains: simply returns a URL for the appropriate directory; it does not guarantee that the directory exists. If you want to perform operations such as writing to the directory, you may have to create it first.

URLForDirectory:inDomain:appropriateForURL:create:error: is a URL-based replacement for FSFindFolder. You can specify and optionally create a directory for a particular purpose (for example, the replacement of a particular item on disk, or a particular Library directory). You may pass only one of the values from the NSSearchPathDomainMask enumeration, and you may not pass NSAllDomainsMask.

Locating Directories as Paths

The following example illustrates how you can use the NSSearchPathForDirectoriesInDomains function to find the current user’s Documents directory:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([paths count] > 0) {
    NSString *userDocumentsPath = [paths objectAtIndex:0];
    // Implementation continues...

NSSearchPathForDirectoriesInDomains simply returns the path of the appropriate directory, it does not guarantee that the directory exists. If you want to perform operations such as writing to the directory, you may have to create it first.

Dealing with Broken Links

Constructing a pathname to a file does not guarantee that the file exists at that path. Specifying a path results in one of the following possibilities:

If the pathname specifies a valid file or link, you can obtain information about the file using the NSFileManager method attributesOfItemAtPath:error: (see Getting and Setting File Attributes). This method does not traverse a link however. You can first call destinationOfSymbolicLinkAtPath:error: and then attributesOfItemAtPath:error:. In Mac OS X v10.6 and later, you can use NSURL methods URLByResolvingSymlinksInPath and resourceValuesForKeys:error: (see Getting and Setting File Attributes).