48

I have this code for creating a folder/directory in Objective-C/cocoa.

if(![fileManager fileExistsAtPath:directory isDirectory:&isDir])
        if(![fileManager createDirectoryAtPath:directory attributes:nil])
            NSLog(@"Error: Create folder failed %@", directory);

It works fine, but I got creatDirectoryAtPath:attributes is deprecated warning message. What's the newest way of making a directory builder in Cocoa/Objective-c?

SOLVED

BOOL isDir;
NSFileManager *fileManager= [NSFileManager defaultManager]; 
if(![fileManager fileExistsAtPath:directory isDirectory:&isDir])
    if(![fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:NULL])
        NSLog(@"Error: Create folder failed %@", directory);
nio
  • 5,141
  • 2
  • 24
  • 35
prosseek
  • 182,215
  • 215
  • 566
  • 871
  • possible duplicate of [createDirectoryAtPath:attributes: alternative](http://stackoverflow.com/questions/3880786/createdirectoryatpathattributes-alternative) – Vladimir Feb 28 '11 at 06:24

4 Answers4

58

Found in the documentation:

-[NSFileManager createDirectoryAtPath:withIntermediateDirectories:attributes:error:]

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
20

Your solution is correct, though Apple includes an important note within NSFileManager.h:

/* The following methods are of limited utility. Attempting to predicate behavior 
based on the current state of the filesystem or a particular file on the 
filesystem is encouraging odd behavior in the face of filesystem race conditions. 
It's far better to attempt an operation (like loading a file or creating a 
directory) and handle the error gracefully than it is to try to figure out ahead 
of time whether the operation will succeed. */

- (BOOL)fileExistsAtPath:(NSString *)path;
- (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDirectory;
- (BOOL)isReadableFileAtPath:(NSString *)path;
- (BOOL)isWritableFileAtPath:(NSString *)path;
- (BOOL)isExecutableFileAtPath:(NSString *)path;
- (BOOL)isDeletableFileAtPath:(NSString *)path;

Essentially, if multiple threads/processes are modifying the file system simultaneously the state could change in between calling fileExistsAtPath:isDirectory: and calling createDirectoryAtPath:withIntermediateDirectories:, so it is superfluous and possibly dangerous to call fileExistsAtPath:isDirectory: in this context.

For your needs and within the limited scope of your question it likely would not be a problem, but the following solution is both simpler and offers less of a chance of future issues arising:

NSFileManager *fileManager= [NSFileManager defaultManager];
NSError *error = nil;
if(![fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:&error]) {
     // An error has occurred, do something to handle it
     NSLog(@"Failed to create directory \"%@\". Error: %@", directory, error);
}

Also note from Apple's documentation:

Return Value

YES if the directory was created, YES if createIntermediates is set and the directory already exists), or NO if an error occurred.

So, setting createIntermediates to YES, which you already do, is a de facto check of whether the directory already exists.

Matt
  • 1,563
  • 12
  • 13
  • This answer should be marked as accepted as it contains a lot more details and IS the right answer : You don't have to use fileExistsAtPath if you have "withIntermediateDirectories:YES" – CtrlX May 01 '16 at 02:49
  • This is indeed the best answer. Just for clarity, if you do not want to have the directory overwritten, set withIntermediateDirectories:NO. This will be documented in the error. – Alex Dec 02 '16 at 06:56
  • Thank you! I was looking for some clarification on the subject. – Julius Jul 26 '17 at 17:36
  • Provides a _de facto check of whether the directory already exists_ when `withIntermediateDirectories` is set to `true` in Swift. (`createDirectory(at: someUrl, withIntermediateDirectories: true`) – marc-medley Aug 21 '21 at 23:45
3

You may prefer to work with the NSFileManager method:

createDirectoryAtURL:withIntermediateDirectories:attributes:error:

It works with URL's instead of path strings.

Stephan
  • 4,263
  • 2
  • 24
  • 33
3

Thought I'd add to this and mention some more from the documentation about using the +defaultManager method:

In iOS and Mac OS X v 10.5 and later you should consider using [[NSFileManager alloc] init] rather than the singleton method defaultManager. Instances of NSFileManager are considered thread-safe when created using [[NSFileManager alloc] init].

August
  • 698
  • 4
  • 12
  • 21
  • 3
    It's safe to use the `defaultManager`. "The methods of the shared NSFileManager object can be called from multiple threads safely. However, if you use a delegate to receive notifications about the status of move, copy, remove, and link operations, you should create a unique instance of the file manager object, assign your delegate to that object, and use that file manager to initiate your operations." – houbysoft Aug 06 '12 at 13:36