Version number comparison with NSScanner (Objective-C + Foundation)
#import "DTVersionComparison.h"
int compareVersions(NSString *version1, NSString *version2){
if(!version1 || !version2 || ![version1 isKindOfClass:[NSString class]] || ![version2 isKindOfClass:[NSString class]])
return 0;
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSScanner *firstScanner = [NSScanner scannerWithString:version1];
NSScanner *secondScanner= [NSScanner scannerWithString:version2];
unsigned int winner = 0;
int firstComponent;
int secondComponent;
BOOL firstDidScan;
BOOL secondDidScan;
NSDictionary *devPhases = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:1], @"d",
[NSNumber numberWithInt:2], @"a",
[NSNumber numberWithInt:3], @"b",
[NSNumber numberWithInt:4], @"f", nil];
while (winner == 0) {
// Scan the next component of each version number
firstDidScan = [firstScanner scanInt:&firstComponent];
secondDidScan = [secondScanner scanInt:&secondComponent];
// Check if either scanner has been exhausted and exit the loop accordingly
if (!firstDidScan && !secondDidScan){
break;
} else if (!firstDidScan && secondDidScan){
winner = 2;
break;
} else if (!secondDidScan && firstDidScan){
winner = 1;
break;
}
// Otherwise, compare what we have just scanned
if (firstComponent > secondComponent) winner = 1;
else if (secondComponent > firstComponent) winner = 2;
// If we're going to go around the loop again, try to advance past the next dot
[firstScanner scanString:@"." intoString:NULL];
[secondScanner scanString:@"." intoString:NULL];
}
// If we have not found a winner yet, look for dev phase components after the version number proper
if(winner == 0){
NSCharacterSet *devPhaseCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"dabf"];
NSString *firstDevComponent;
firstDidScan = [firstScanner scanCharactersFromSet:devPhaseCharacterSet intoString:&firstDevComponent];
NSString *secondDevComponent;
secondDidScan = [secondScanner scanCharactersFromSet:devPhaseCharacterSet intoString:&secondDevComponent];
if (firstDidScan && !secondDidScan)
winner = 2;
else if (secondDidScan && !firstDidScan)
winner = 1;
else if (firstDidScan && secondDidScan){
int firstDevWeight = [[devPhases objectForKey:[firstDevComponent substringToIndex:1]] intValue];
int secondDevWeight = [[devPhases objectForKey:[secondDevComponent substringToIndex:1]] intValue];
if (firstDevWeight > secondDevWeight)
winner = 1;
else if (secondDevWeight > firstDevWeight)
winner = 2;
else if (firstDevWeight == secondDevWeight){
firstDidScan = [firstScanner scanInt:&firstComponent];
secondDidScan = [secondScanner scanInt:&secondComponent];
if(firstDidScan && !secondDidScan)
winner = 1;
else if (secondDidScan && !firstDidScan)
winner = 2;
else if (firstDidScan && secondDidScan){
if (firstComponent > secondComponent)
winner = 1;
else if (secondComponent > firstComponent)
winner = 2;
}
}
}
}
// If we have not found a winner yet, look for a count of commits since the version (a la `git describe`)
if(winner == 0){
int firstCommitCount = 0;
int secondCommitCount = 0;
if ((firstDidScan = [firstScanner scanString:@"-" intoString:NULL])) {
firstDidScan = [firstScanner scanInt:&firstCommitCount];
}
if ((secondDidScan = [secondScanner scanString:@"-" intoString:NULL])) {
secondDidScan = [secondScanner scanInt:&secondCommitCount];
}
if (firstDidScan || secondDidScan) {
if (firstCommitCount > secondCommitCount) {
winner = 1;
} else if (firstCommitCount < secondCommitCount) {
winner = 2;
}
}
}
[pool drain];
return winner;
}
#import <Foundation/Foundation.h>
int compareVersions(NSString *version1, NSString *version2);