Sorting Like the Finder

Something that most Cocoa programmers are likely encounter in their lifetimes is trying to sort a list like the Finder. At first sight, it might seem like there’s nothing special about how the Finder sorts; it looks like a plain old alphabetical sort. But it isn’t, there are many subtleties with the way in which the Finder sorts a list of documents. A simple example of this is when you have three documents called “1”,”2″, and “10”. A normal alphabetical sort would order them: “1”,”10″,”2″. But the Finder is smart enough to order them: “1”,”2″,”10″.

The basic code to sort like the Finder comes from Apple’s Technical Q&A QA1159. Using this code, I created a category for NSString which adds a method called finderCompare: to the NSString class. This way you only need to create an instance of NSSortDescriptor and initialize it using initWithKey:ascending:selector: where selector: should be @selector(finderCompare:). Then you just need to use this instance of NSSortDescriptor whenever you want to sort something like the Finder.

You can find the code for the category below, or you can just click here to download the file. To use this file you only need to add it to your Xcode project.

#import 

@interface NSString (FinderCompare)

- (NSComparisonResult)finderCompare:(NSString *)aString;

@end

@implementation NSString (FinderCompare)

- (NSComparisonResult)finderCompare:(NSString *)aString
{
	SInt32 compareResult;
	
	CFIndex lhsLen = [self length];;
        CFIndex rhsLen = [aString length];
	
	UniChar *lhsBuf = malloc(lhsLen * sizeof(UniChar));
	UniChar *rhsBuf = malloc(rhsLen * sizeof(UniChar));
	
	[self getCharacters:lhsBuf];
	[aString getCharacters:rhsBuf];
	
       (void) UCCompareTextDefault(
          kUCCollateComposeInsensitiveMask
        | kUCCollateWidthInsensitiveMask
        | kUCCollateCaseInsensitiveMask
        | kUCCollateDigitsOverrideMask
        | kUCCollateDigitsAsNumberMask
        | kUCCollatePunctuationSignificantMask,
        lhsBuf,
        lhsLen,
        rhsBuf,
        rhsLen,
        NULL,
        &compareResult
       );
	
	free(lhsBuf);
	free(rhsBuf);
	
	return (CFComparisonResult) compareResult;
}

@end