enumeratorAtURL:includingPropertiesForKeys

Hello,

I don’t understand the proper parameter syntax of this method. My target is to obtain a list of regular files from the Downloads folder (URL “pathToDownload” is OK), excluding the folders.

I already tried the predicate (without success, for me “predicateWithString” is too obscure) and I was wondering if enumeratorAtURL could filter this itself.

self.theList = [[manager enumeratorAtURL:pathToDownload includingPropertiesForKeys:[NSArray arrayWithObject:NSURLIsRegularFileKey] options:nil errorHandler:nil] allObjects];

Of course, nothing works, time flies and the night become shorter.

Can you help me?

Try changing options to 0 or NSDirectoryEnumerationSkipsHiddenFiles.

Hi shane,

Changing options gives something terrible:

Thread 1: EXC_BAD_ACCESS (code=13,address=0x0)

I suppose this is close to the good old bomb icon.

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 com.apple.CoreFoundation 0x00007fff8b83cd40 _CFURLIsFileReferenceURL + 64
1 com.apple.CoreServicesInternal 0x00007fff8560925d _URLEnumeratorCreateForDirectoryURL + 91
2 com.apple.Foundation 0x00007fff8aef94df -[NSURLDirectoryEnumerator initWithURL:includingPropertiesForKeys:options:errorHandler:] + 128
3 com.apple.Foundation 0x00007fff8aef943e -[NSFileManager enumeratorAtURL:includingPropertiesForKeys:options:errorHandler:] + 132

it looks like it’s shaken out a dodgy URL. Where does the URL come from?

From here:

manager = [NSFileManager defaultManager];
NSString *s =[ NSHomeDirectory() stringByAppendingPathComponent:@"/Downloads/"];

[manager changeCurrentDirectoryPath: s];
pathToDownload = [NSURL URLWithString:s];

Note that everything is OK when I use enumeratorAtPath method. Then I make an ugly IF to filter the result. That’s what I wanted to avoid.

In Cocoa, when it’s hard. it’s wrong.

Your problem is URLWithString: – you should be using fileURLWithPath:.

And what are you trying to achieve with changeCurrentDirectoryPath?

Changed. Nothing but one more bomb icon.

Nothing particular. I removed it.

The enumeratorAtPath still returns “.DS_Store” for an empty folder, which is correct.

I’m not so sure, after reading the docs, that enumeratorAtURL: includingPropertiesForKeys is what I’m after. This method does not seem to filter the array.

It does – just not in the way you want. It can filter out invisible files, and the contents of subdirectories and/or packages.

But if you want a list of just files, you are going to have to, well, enumerate. This might help:

NSDirectoryEnumerationOptions enumOpts =  NSDirectoryEnumerationSkipsHiddenFiles | NSDirectoryEnumerationSkipsPackageDescendants;
NSMutableArray *keysToGet = [NSMutableArray arrayWithObject:NSURLIsDirectoryKey];
NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtURL:url
                                               includingPropertiesForKeys:keysToGet
                                                                  options:enumOpts
                                                             errorHandler:nil];
      
NSNumber *isDirectory;
BOOL isFileOrPackage;
     
for (NSURL *theURL in dirEnumerator) {
 [theURL getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];
 if (([isDirectory boolValue] == NO) || ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:[theURL path]])) {
            // it's a file or package

the result of enumeratorAtURL:includingPropertiesForKeys:options:errorHandler: is NOT an array.
Look into the documentation (NSFileManager Class Reference). There is an example how to handle the method

That’s true, but maybe you missed the allObjects.

Indeed. :slight_smile:

But there is a general misunderstanding. I guess fiz assumes that the enumerator method filters the items in the directory depending on the includingProperties array like NSPredicate does.

@fiz: All enumeratorAt. and contentsOf. methods of NSFileManager do not provide a function similar to NSPredicate except some of them can skip subdirectory descendants, package descendants and hidden files

Thank you for the discussion. Finally I use these methods (two fast enumerators and a multiple-condition IF:

Question 1:

In Pascal, we had a short-circuit evaluation. Note there are three conditions in the example above. If one is FALSE, then the entire expression is FALSE.
if (condition1 and condition2 and condition3) then. was fully evaluated.
if (condition1 & condition2 & condition3) then. was evaluated condition by condition and set to FALSE at first FALSE condition.

In Objective-C, && is always short-circuit, correct?

Question 2:

There is no way to get the size of a folder, but was is the ObjC equivalent of the ASOC:

set theSize to size of (info for theFolder as alias) -- which gives correct results ?

Yes, logical comparisons short-circuit.

You have to enumerate and add the values; there’s no simple command. Here’s some Carbon code that seems pretty quick:

www.cocoabuilder.com/archive/cocoa/136498-obtain-directory-size.html#136503

Nothing I can get working. How does ASOC do this? It must be a lot of C code added by the compiler.

to use the routine on CocoaBuilder mentioned by Shane put the code as a method in the same class as the caller.
For example to retrieve the size of the Downloads folder, write


    NSURL *downloadsFolderURL = [NSURL fileURLWithPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Downloads"]];
    FSRef fsRef;
    unsigned long long folderSize = 0;
    if (CFURLGetFSRef((CFURLRef)downloadsFolderURL, &fsRef)) {
        folderSize = [self fastFolderSizeAtFSRef:&fsRef]; 
         NSLog(@"%lld", folderSize);
    }

This might be a bit easier to follow:

github.com/davedelong/BetterInfo/blob/aa1cfe079dad6207a7ddac84b108a768c2cc7156/NSFileManager+BetterInfo.m

But don’t become too attached to everything in Carbon…