34

Is there an method to get the contents of a folder in a particular order? I'd like an array of file attribute dictionaries (or just file names) ordered by date modified.

Right now, I'm doing it this way:

  • get an array with the file names
  • get the attributes of each file
  • store the file's path and modified date in a dictionary with the date as a key

Next I have to output the dictionary in date order, but I wondered if there's an easier way? If not, is there a code snippet somewhere which will do this for me?

Thanks.

nevan king
  • 112,709
  • 45
  • 203
  • 241

5 Answers5

36

nall's code above pointed me in the right direction, but I think there are some mistakes in the code as posted above. For instance:

  1. Why is filesAndProperties allocated using NMutableDictonary rather than an NSMutableArray?

  2. 
    NSDictionary* properties = [[NSFileManager defaultManager]
                                            attributesOfItemAtPath:NSFileModificationDate
                                            error:&error];
    
    
    The code above is passing the wrong parameter for attributesOfItemAtPath - it should be attributesOfItemAtPath:path

  3. Your are sorting the files array, but you should be sorting filesAndProperties.


I have implemented the same, with corrections, and using blocks and posted below:


    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* documentsPath = [searchPaths objectAtIndex: 0]; 

    NSError* error = nil;
    NSArray* filesArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsPath error:&error];
    if(error != nil) {
        NSLog(@"Error in reading files: %@", [error localizedDescription]);
        return;
    }

    // sort by creation date
    NSMutableArray* filesAndProperties = [NSMutableArray arrayWithCapacity:[filesArray count]];
    for(NSString* file in filesArray) {
        NSString* filePath = [iMgr.documentsPath stringByAppendingPathComponent:file];
        NSDictionary* properties = [[NSFileManager defaultManager]
                                    attributesOfItemAtPath:filePath
                                    error:&error];
        NSDate* modDate = [properties objectForKey:NSFileModificationDate];

        if(error == nil)
        {
            [filesAndProperties addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                           file, @"path",
                                           modDate, @"lastModDate",
                                           nil]];                 
        }
    }

        // sort using a block
        // order inverted as we want latest date first
    NSArray* sortedFiles = [filesAndProperties sortedArrayUsingComparator:
                            ^(id path1, id path2)
                            {                               
                                // compare 
                                NSComparisonResult comp = [[path1 objectForKey:@"lastModDate"] compare:
                                                           [path2 objectForKey:@"lastModDate"]];
                                // invert ordering
                                if (comp == NSOrderedDescending) {
                                    comp = NSOrderedAscending;
                                }
                                else if(comp == NSOrderedAscending){
                                    comp = NSOrderedDescending;
                                }
                                return comp;                                
                            }];

M-V
  • 5,167
  • 7
  • 52
  • 55
  • To invert the ordering you can just multiply the comparison result by -1. – Jonathan. Oct 01 '11 at 14:01
  • 5
    To invert the ordering you can just use `return [[path2 objectForKey:@"lastModDate"] compare:[path1 objectForKey:@"lastModDate"]];` –  Jan 06 '12 at 15:44
20

How about this:

// Application documents directory
NSURL *documentsURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];

NSArray *directoryContent = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:documentsURL
                                                          includingPropertiesForKeys:@[NSURLContentModificationDateKey]
                                                                             options:NSDirectoryEnumerationSkipsHiddenFiles
                                                                               error:nil];

NSArray *sortedContent = [directoryContent sortedArrayUsingComparator:
                        ^(NSURL *file1, NSURL *file2)
                        {
                            // compare
                            NSDate *file1Date;
                            [file1 getResourceValue:&file1Date forKey:NSURLContentModificationDateKey error:nil];

                            NSDate *file2Date;
                            [file2 getResourceValue:&file2Date forKey:NSURLContentModificationDateKey error:nil];

                            // Ascending:
                            return [file1Date compare: file2Date];
                            // Descending:
                            //return [file2Date compare: file1Date];
                        }];
Ivan Kovacevic
  • 1,322
  • 12
  • 30
  • 11
    I was just looking for the solution for this myself and ended up using the above code. So I've put it here cause I think its cleaner than what others have provided ;) – Ivan Kovacevic Nov 01 '12 at 10:48
10

Simpler...

NSArray*  filelist_sorted;
filelist_sorted = [filelist_raw sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
    NSDictionary* first_properties  = [[NSFileManager defaultManager] attributesOfItemAtPath:[NSString stringWithFormat:@"%@/%@", path_thumb, obj1] error:nil];
    NSDate*       first             = [first_properties  objectForKey:NSFileModificationDate];
    NSDictionary* second_properties = [[NSFileManager defaultManager] attributesOfItemAtPath:[NSString stringWithFormat:@"%@/%@", path_thumb, obj2] error:nil];
    NSDate*       second            = [second_properties objectForKey:NSFileModificationDate];
    return [second compare:first];
}];
Mark Yang
  • 226
  • 3
  • 16
6

It's too slow

[[NSFileManager defaultManager]
                                attributesOfItemAtPath:NSFileModificationDate
                                error:&error];

Try this code:

+ (NSDate*) getModificationDateForFileAtPath:(NSString*)path {
    struct tm* date; // create a time structure
    struct stat attrib; // create a file attribute structure

    stat([path UTF8String], &attrib);   // get the attributes of afile.txt

    date = gmtime(&(attrib.st_mtime));  // Get the last modified time and put it into the time structure

    NSDateComponents *comps = [[NSDateComponents alloc] init];
    [comps setSecond:   date->tm_sec];
    [comps setMinute:   date->tm_min];
    [comps setHour:     date->tm_hour];
    [comps setDay:      date->tm_mday];
    [comps setMonth:    date->tm_mon + 1];
    [comps setYear:     date->tm_year + 1900];

    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDate *modificationDate = [[cal dateFromComponents:comps] addTimeInterval:[[NSTimeZone systemTimeZone] secondsFromGMT]];

    [comps release];

    return modificationDate;
}
FreeAsInBeer
  • 12,937
  • 5
  • 50
  • 82
abuharsky
  • 1,151
  • 12
  • 21
  • This is much faster. 4x faster in my tests. I ran into one bug with this though. My gmtime is returning UTC not GMT. Some times had a one hour difference which caused problems. – ski_squaw Oct 05 '13 at 16:33
  • Adjust code as follows: NSCalendar *cal = [NSCalendar currentCalendar]; NSTimeZone *tz = [NSTimeZone timeZoneWithAbbreviation:[[NSString alloc] initWithUTF8String:date->tm_zone]]; cal.timeZone = tz; – ski_squaw Oct 05 '13 at 16:35
  • You need to #import "sys/stat.h" – Maciej Swic Oct 09 '14 at 09:20
0

Code does not work in iPhone SDK, full of compilation error. Please find updated code `

NSInteger lastModifiedSort(id path1, id path2, void* context)
{
    int comp = [[path1 objectForKey:@"lastModDate"] compare:
     [path2 objectForKey:@"lastModDate"]];
    return comp;
}

-(NSArray *)filesByModDate:(NSString*) path{

    NSError* error = nil;

    NSArray* filesArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path
                                                                         error:&error];
    if(error == nil)
    {
        NSMutableArray* filesAndProperties = [NSMutableArray arrayWithCapacity:[filesArray count]];

        for(NSString* imgName in filesArray)
        {

            NSString *imgPath = [NSString stringWithFormat:@"%@/%@",path,imgName];
            NSDictionary* properties = [[NSFileManager defaultManager]
                                        attributesOfItemAtPath:imgPath
                                        error:&error];

            NSDate* modDate = [properties objectForKey:NSFileModificationDate];

            if(error == nil)
            {
                [filesAndProperties addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                               imgName, @"path",
                                               modDate, @"lastModDate",
                                               nil]];                     
            }else{
                NSLog(@"%@",[error description]);
            }
        }
        NSArray* sortedFiles = [filesAndProperties sortedArrayUsingFunction:&lastModifiedSort context:nil];

        NSLog(@"sortedFiles: %@", sortedFiles);      
        return sortedFiles;
    }
    else
    {
        NSLog(@"Encountered error while accessing contents of %@: %@", path, error);
    }

    return filesArray;
}

`

Community
  • 1
  • 1
nall
  • 15,899
  • 4
  • 61
  • 65
  • Thanks for the answer. There seems to be a few things that won't work with the iPhone SDK here (NSURLContentModificationDateKey and some other NSURL methods). – nevan king Oct 06 '09 at 07:24
  • I've started trying to change it to work with the iPhone SDK, but getResourceValue doesn't exist there. I'll look for an array sort for iPhone. – nevan king Oct 06 '09 at 07:31
  • nevan, I updated this to work within the constraints of the iPhone SDK – nall Oct 06 '09 at 13:15
  • Lots of little bugs in this code fragment ... I don't think this code is tested on iOS. – Roger Dec 01 '10 at 11:10