2

I've found the following code at http://snipplr.com/view/2771

Which is pretty good, almost exactly what I was looking for, but if I use the values @"1.4.5", @"10.4" it produces the wrong result, saying that the first number is lower.

Arghhhh Late night coding, sorry I read 10.4 as 1.4 :(

I'm unsure why compare is having an issue and what the problem is ?

/*
 * compareVersions(@"10.4",             @"10.3"); //             
       returns NSOrderedDescending (1) - aka first number is higher

 * compareVersions(@"10.5",             @"10.5.0"); //           
       returns NSOrderedSame (0) 

 * compareVersions(@"10.4 Build 8L127", @"10.4 Build 8P135"); // 
       returns NSOrderedAscending (-1) - aka first number is lower
 */
NSComparisonResult compareVersions(NSString* leftVersion, NSString* rightVersion)
{
    int i;

    // Break version into fields (separated by '.')
    NSMutableArray *leftFields  = [[NSMutableArray alloc] initWithArray:[leftVersion  componentsSeparatedByString:@"."]];
    NSMutableArray *rightFields = [[NSMutableArray alloc] initWithArray:[rightVersion componentsSeparatedByString:@"."]];

    // Implict ".0" in case version doesn't have the same number of '.'
    if ([leftFields count] < [rightFields count]) {
        while ([leftFields count] != [rightFields count]) {
            [leftFields addObject:@"0"];
        }
    } else if ([leftFields count] > [rightFields count]) {
        while ([leftFields count] != [rightFields count]) {
            [rightFields addObject:@"0"];
        }
    }

.

    // Do a numeric comparison on each field
    for(i = 0; i < [leftFields count]; i++) {
        NSComparisonResult result = [[leftFields objectAtIndex:i] compare:[rightFields objectAtIndex:i] options:NSNumericSearch];
        if (result != NSOrderedSame) {
            [leftFields release];
            [rightFields release];
            return result;
        }
    }

    [leftFields release];
    [rightFields release];  
    return NSOrderedSame;
}
Jules
  • 7,568
  • 14
  • 102
  • 186
  • Why would you expect 10.4 to be lower than 1.4.5? That's usually not how version numbers work... – omz Sep 06 '12 at 21:32
  • Sorry, stupid question probably, but what test do you want to apply? What makes 1.4.5 larger than 10.4? – Tommy Sep 06 '12 at 21:32
  • 1
    Those aren't *version numbers* they are *version paths*. Once you treat them as a path, treating each corresponding element as a sole comparison, then the solution should be obvious. And, yeah, `1.4.5` vs. `10.4` better yield `10.4` as the higher version or you are out of luck because your *version paths* are now no longer coherent. – bbum Sep 07 '12 at 03:05

9 Answers9

2

[I posted this earlier today, but it was not selected as the answer, and it may be more appropriate to your problem. There are other techniques, you can look here and here for other solutions.]

What I do is take that string and break it into components:

NSArray *array = [myVersion componentsSeparatedByCharactersInSet:@"."];

NSInteger value = 0;
NSInteger multiplier = 1000000;
for(NSString *n in array) {
  value += [n integerValue] * multiplier;
  multiplier /= 100;
}

What this does is give you a normalized value you can use for comparison, and will generally compare releases that have different "depths", ie 1.5 and 1.5.2.

It breaks if you have more than 100 point releases (ie any number is > 100) and also will declare 1.5.0 == 1.5. That said, its short, sweet, and simple to use.

EDIT: if you use the NSString 'compare:options:' method, make sure you have your string well groomed:

    s1 = @"1.";
    s2 = @"1";
    NSLog(@"Compare %@ to %@ result %d", s1, s2, (int)[s1 compare:s2 options:NSNumericSearch]);
    s1 = @"20.20.0";
    s2 = @"20.20";
    NSLog(@"Compare %@ to %@ result %d", s1, s2, (int)[s1 compare:s2 options:NSNumericSearch]);

2012-09-06 11:26:24.793 xxx[59804:f803] Compare 1. to 1 result 1
2012-09-06 11:26:24.794 xxx[59804:f803] Compare 20.20.0 to 20.20 result 1
Community
  • 1
  • 1
David H
  • 40,852
  • 12
  • 92
  • 138
  • 1
    Yeah, I've used this same basic approach in the past and found it reasonably robust, so long as you are careful to not use individual numbers larger than your multiplier granularity. (If you use 256 then each component will fit in a byte.) – Hot Licks Sep 06 '12 at 21:38
1

The Sparkle framework for Mac is open source, and it has some neat version checking code you can have a look at: https://github.com/andymatuschak/Sparkle/blob/master/SUStandardVersionComparator.m

Tom Irving
  • 10,041
  • 6
  • 47
  • 63
0

so you want to compare 10.5 to 1.4.6 such that 10.5 is viewed as 0.10.5

if this is the case, you need to add "0" array items to the left side of your separated version number

NSComparisonResult compareVersions(NSString* leftVersion, NSString* rightVersion)
{
    int i;

    // Break version into fields (separated by '.')
    NSMutableArray *leftFields  = [[NSMutableArray alloc] initWithArray:[leftVersion  componentsSeparatedByString:@"."]];
    NSMutableArray *rightFields = [[NSMutableArray alloc] initWithArray:[rightVersion componentsSeparatedByString:@"."]];

    // Implict "0" in case version doesn't have the same number of '.'
    if ([leftFields count] < [rightFields count]) {
        while ([leftFields count] != [rightFields count]) {
            [leftFields insertObject:@"0" atIndex:0];
        }
    } else if ([leftFields count] > [rightFields count]) {
        while ([leftFields count] != [rightFields count]) {
            [rightFields insertObject:@"0" atIndex:0];
        }
    }
Kyle
  • 434
  • 2
  • 10
0

I'm not 100% sure what you're asking, but if you're looking to sort numbers regardless of how many periods "." are in the number you can use an NSSortDescriptor on a dictionary of OS versions:

NSArray *originalArray = [NSArray arrayWithObjects:
              [NSDictionary dictionaryWithObject:@"10.4.5" forKey:@"version"],
              [NSDictionary dictionaryWithObject:@"10.5.6" forKey:@"version"],
              [NSDictionary dictionaryWithObject:@"10.6.8" forKey:@"version"],
              [NSDictionary dictionaryWithObject:@"10.8" forKey:@"version"],
              [NSDictionary dictionaryWithObject:@"10.7.1" forKey:@"version"],
              [NSDictionary dictionaryWithObject:@"10.8.2" forKey:@"version"],
              nil];

NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"version" ascending:true];
NSArray *sortedArray = [originalArray sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
NSLog(@"Lowest to highest: %@", sortedArray);
NSLog(@"Highest OS version: %@",[[sortedArray objectAtIndex:[sortedArray indexOfObject:[sortedArray lastObject]]] objectForKey:@"version"]);
Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
0

My sense would be that the first numeral group is always the most significant, so 10.anything is bigger than 9.anything.anything. If I'm right about that, then the solution is to replace the dots with zeros and pad the shorter string on the right-hand side with zeros to match the length of the longer string:

e.g.
9.4   --->  90400  (padded on the right with 00)
8.6.7 --->  80607

What's nice about this is, that if I'm wrong about the requirement, the algorithm can be readily fixed by padding the shorter string on the right.

- (NSComparisonResult)compareVersion:(NSString *)vA withVersion:(NSString *)vB {

    NSString *vAPadded = [vA stringByReplacingOccurrencesOfString:@"." withString:@"0"];
    NSString *vBPadded = [vB stringByReplacingOccurrencesOfString:@"." withString:@"0"];

    while (vAPadded.length < vBPadded.length)
        vAPadded = [vAPadded stringByAppendingString:@"0"];

    while (vBPadded.length < vAPadded.length)
        vBPadded = [vBPadded stringByAppendingString:@"0"];

    return [vAPadded intValue] - [vBPadded intValue];
}

If I've got the requirement backwards on significant digits, change the pads like this:

vAPadded = [@"0" stringByAppendingString:vAPadded];
danh
  • 62,181
  • 10
  • 95
  • 136
0

Why don't use NSString compare:options:NSNumericSearch

NSString *sysVer = [[UIDevice currentDevice] systemVersion];
NSLog(@"%@,%d,%d,%d", sysVer, [sysVer compare:@"1.0" options: NSNumericSearch], [sysVer compare:@"6.0" options: NSNumericSearch],[sysVer compare:@"10.0" options: NSNumericSearch]);
if ([sysVer compare:@"6.0" options: NSNumericSearch]>=NSOrderedSame) {
    NSLog(@"ios 6");
}
Baryon Lee
  • 1,157
  • 11
  • 11
0

You can use my Version class that helps you to parse your version strings into Version objects for easy compare. It supports 4 field version numbers like major.minor.release.build, all fields are optional. Also, it has a compare method for comapring two version objects.

https://github.com/polatolu/version-ios

bateristt
  • 176
  • 15
0

it's simple with VersionComparator Class

https://github.com/danhanly/VersionComparator

BOOL greater = [VersionComparator isVersion:@"2.0.0" greaterThanVersion:@"1.1.0"];

Carlos Avalos
  • 247
  • 4
  • 8
0

As answered in this post; Compare version numbers in Objective-C

Check out my NSString category that implements easy version checking on github; https://github.com/stijnster/NSString-compareToVersion

[@"1.2.2.4" compareToVersion:@"1.2.2.5"];

This will return a NSComparisonResult which is more accurate then using;

[@"1.2.2" compare:@"1.2.2.5" options:NSNumericSearch]

Helpers are also added;

[@"1.2.2.4" isOlderThanVersion:@"1.2.2.5"];
[@"1.2.2.4" isNewerThanVersion:@"1.2.2.5"];
[@"1.2.2.4" isEqualToVersion:@"1.2.2.5"];
[@"1.2.2.4" isEqualOrOlderThanVersion:@"1.2.2.5"];
[@"1.2.2.4" isEqualOrNewerThanVersion:@"1.2.2.5"];
Community
  • 1
  • 1
Stijnster
  • 209
  • 2
  • 7