35

How do you delete all the contents of a directory without deleting the directory itself? I want to basically empty a folder yet leave it (and the permissions) intact.

Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120
Vervious
  • 5,471
  • 3
  • 35
  • 53

9 Answers9

84

E.g. by using a directory enumerator:

NSFileManager *fileManager = [[NSFileManager alloc] init];
NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:path];    
NSString *file;

while (file = [enumerator nextObject]) {
    NSError *error = nil;
    BOOL result = [fileManager removeItemAtPath:[path stringByAppendingPathComponent:file] error:&error];

    if (!result && error) {
        NSLog(@"Error: %@", error);
    }
}

Swift

let fileManager = NSFileManager.defaultManager()
let enumerator = fileManager.enumeratorAtURL(cacheURL, includingPropertiesForKeys: nil, options: nil, errorHandler: nil)

while let file = enumerator?.nextObject() as? String {
    fileManager.removeItemAtURL(cacheURL.URLByAppendingPathComponent(file), error: nil)
}
Iulian Onofrei
  • 7,489
  • 8
  • 59
  • 96
Georg Fritzsche
  • 93,086
  • 26
  • 183
  • 232
  • 2
    Don't forget to check whether `removeItemAtPath:` actually failed before attempting to use the error object. At the very least, you may report more errors than you actually have. – Peter Hosey May 08 '10 at 03:35
  • There's a problem in your while loop, you can't declare you variable inside the while parentheses !! – Psycho Jul 19 '11 at 13:36
  • 3
    @Psycho: True for Objective-C, works fine though for Objective-C++. Worth a down-vote when this isn't related to the problem and easy to fix? I don't think so... – Georg Fritzsche Jul 19 '11 at 13:40
  • The down vote was for the solution, just delete the whole directory by saving the permissions instead of iterating through the content. – Psycho Jul 19 '11 at 13:45
11

Try this:

NSFileManager *manager = [NSFileManager defaultManager];
NSString *dirToEmpty = ... //directory to empty
NSError *error = nil;
NSArray *files = [manager contentsOfDirectoryAtPath:dirToEmpty 
                                              error:&error];

if(error) {
  //deal with error and bail.
}

for(NSString *file in files) {
    [manager removeItemAtPath:[dirToEmpty stringByAppendingPathComponent:file]
                        error:&error];
    if(error) {
       //an error occurred...
    }
}    
petert
  • 6,552
  • 3
  • 34
  • 45
Jacob Relkin
  • 151,673
  • 29
  • 336
  • 313
  • `contentsOfDirectoryAtPath::` doesn't give you the full path of the contents. – Georg Fritzsche May 05 '10 at 01:43
  • There is no need to test for "." or ".." since `contentsOfDirectoryAtPath:error` _filters_ them out; from docs **The search is shallow and therefore does not return the contents of any subdirectories. This returned array does not contain strings for the current directory (“.”), parent directory (“..”), or resource forks (begin with “._”) and does not traverse symbolic links.** – petert Nov 24 '11 at 10:55
  • @petert Whoa, this is a very old answer. Thanks for the info. – Jacob Relkin Nov 24 '11 at 10:59
5

in swift 2.0:

if let enumerator = NSFileManager.defaultManager().enumeratorAtPath(dataPath) {
  while let fileName = enumerator.nextObject() as? String {
    do {
        try NSFileManager.defaultManager().removeItemAtPath("\(dataPath)\(fileName)")
    }
    catch let e as NSError {
      print(e)
    }
    catch {
      print("error")
    }
  }
}
Max
  • 319
  • 2
  • 8
3

Swift 2.1.1:

public func deleteContentsOfFolder()
{
    // folderURL
    if let folderURL = self.URL()
    {
        // enumerator
        if let enumerator = NSFileManager.defaultManager().enumeratorAtURL(folderURL, includingPropertiesForKeys: nil, options: [], errorHandler: nil)
        {
            // item
            while let item = enumerator.nextObject()
            {
                // itemURL
                if let itemURL = item as? NSURL
                {
                    do
                    {
                        try NSFileManager.defaultManager().removeItemAtURL(itemURL)
                    }
                    catch let error as NSError
                    {
                        print("JBSFile Exception: Could not delete item within folder.  \(error)")
                    }
                    catch
                    {
                        print("JBSFile Exception: Could not delete item within folder.")
                    }
                }
            }
        }
    }
}
John Bushnell
  • 1,723
  • 21
  • 27
3

Swift 3 if anyone needs it for a quick cut/paste

let fileManager = FileManager.default
let fileUrls = fileManager.enumerator(at: folderUrl, includingPropertiesForKeys: nil)
while let fileUrl = fileUrls?.nextObject() {
    do {
        try fileManager.removeItem(at: fileUrl as! URL)
    } catch {
        print(error)
    }
}
Travis M.
  • 10,360
  • 1
  • 50
  • 70
2

You can extend the NSFileManager like this:

extension NSFileManager {
  func clearFolderAtPath(path: String) -> Void {
      for file in subpathsOfDirectoryAtPath(path, error: nil) as? [String] ?? []  {
          self.removeItemAtPath(path.stringByAppendingPathComponent(file), error: nil)
      }
  }
}

Then, you can clear the folder like this: NSFileManager.defaultManager().clearFolderAtPath("the folder's path")

Iulian Onofrei
  • 7,489
  • 8
  • 59
  • 96
KaKa
  • 519
  • 6
  • 17
2

The documentation for contentsOfDirectoryAtPath:error: says:

The search is shallow and therefore does not return the contents of any subdirectories. This returned array does not contain strings for the current directory (“.”), parent directory (“..”), or resource forks (begin with “._”) and does not traverse symbolic links.

Thus:

---( file != @"." && file != @".." )---

is irrelevant.

petert
  • 6,552
  • 3
  • 34
  • 45
Mark Perkins
  • 206
  • 1
  • 8
1

Georg Fritzsche answer for Swift did not work for me. Instead of reading the enumerated object as a String, read it as NSURL.

let fileManager = NSFileManager.defaultManager()
let url = NSURL(string: "foo/bar")
let enumerator = fileManager.enumeratorAtURL(url, includingPropertiesForKeys: nil, options: nil, errorHandler: nil)
while let file = enumerator?.nextObject() as? NSURL {
    fileManager.removeItemAtURL(file, error: nil)
}
jathu
  • 31
  • 2
  • 9
0

Why not deleting the whole directory and recreate afterwards? Just get the file attributes and permissions before deleting it, and then recreate it with the same attributes.

Psycho
  • 1,863
  • 1
  • 16
  • 17
  • 1
    Why would you want to remove the folder and run into possible problems (e.g. permissions) if you can just remove it's contents? – Georg Fritzsche Jul 19 '11 at 14:03
  • 1
    It's often better to scan a directory and remove files from source code since it's far more reliable to recover and report errors. – petert Nov 24 '11 at 11:10