J'écris une application qui reçoit des données avec des éléments et des numéros de version. Les nombres sont formatés comme "1.0.1" ou "1.2.5". Comment comparer ces numéros de version? Je pense qu'ils doivent d'abord être formatés en chaîne, non? Quelles options ai-je pour déterminer que "1.2.5" vient après "1.0.1"?
C'est le moyen le plus simple de comparer les versions, en gardant à l'esprit que "1" <"1.0" <"1.0.0":
NSString* requiredVersion = @"1.2.0";
NSString* actualVersion = @"1.1.5";
if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
// actualVersion is lower than the requiredVersion
}
J'ajouterai ma méthode, qui compare les versions strictement numériques (pas de a, b, RC, etc.) avec un certain nombre de composants.
+ (NSComparisonResult)compareVersion:(NSString*)versionOne toVersion:(NSString*)versionTwo {
NSArray* versionOneComp = [versionOne componentsSeparatedByString:@"."];
NSArray* versionTwoComp = [versionTwo componentsSeparatedByString:@"."];
NSInteger pos = 0;
while ([versionOneComp count] > pos || [versionTwoComp count] > pos) {
NSInteger v1 = [versionOneComp count] > pos ? [[versionOneComp objectAtIndex:pos] integerValue] : 0;
NSInteger v2 = [versionTwoComp count] > pos ? [[versionTwoComp objectAtIndex:pos] integerValue] : 0;
if (v1 < v2) {
return NSOrderedAscending;
}
else if (v1 > v2) {
return NSOrderedDescending;
}
pos++;
}
return NSOrderedSame;
}
Il s'agit d'une extension de la réponse de Nathan de Vries pour résoudre le problème de 1 <1.0 <1.0.0 etc.
Tout d'abord, nous pouvons résoudre le problème des ".0" supplémentaires sur notre chaîne de version avec une catégorie NSString
:
@implementation NSString (VersionNumbers)
- (NSString *)shortenedVersionNumberString {
static NSString *const unnecessaryVersionSuffix = @".0";
NSString *shortenedVersionNumber = self;
while ([shortenedVersionNumber hasSuffix:unnecessaryVersionSuffix]) {
shortenedVersionNumber = [shortenedVersionNumber substringToIndex:shortenedVersionNumber.length - unnecessaryVersionSuffix.length];
}
return shortenedVersionNumber;
}
@end
Avec la catégorie NSString
ci-dessus, nous pouvons raccourcir nos numéros de version pour supprimer les .0 inutiles
NSString* requiredVersion = @"1.2.0";
NSString* actualVersion = @"1.1.5";
requiredVersion = [requiredVersion shortenedVersionNumberString]; // now 1.2
actualVersion = [actualVersion shortenedVersionNumberString]; // still 1.1.5
Maintenant, nous pouvons toujours utiliser l'approche magnifiquement simple proposée par Nathan de Vries:
if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
// actualVersion is lower than the requiredVersion
}
Sparkle (le cadre de mise à jour logicielle le plus populaire pour MacOS) a une classe SUStandardVersionComparator qui fait cela, et prend également en compte les numéros de build et les marqueurs bêta. C'est à dire. il compare correctement 1.0.5 > 1.0.5b7
ou 2.0 (2345) > 2.0 (2100)
. Le code utilise uniquement Foundation, donc devrait également fonctionner correctement sur iOS.
Consultez ma catégorie NSString qui implémente la vérification de version facile sur github; https://github.com/stijnster/NSString-compareToVersion
[@"1.2.2.4" compareToVersion:@"1.2.2.5"];
Cela renverra un NSComparisonResult qui est plus précis que l'utilisation;
[@"1.2.2" compare:@"1.2.2.5" options:NSNumericSearch]
Des assistants sont également ajoutés;
[@"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"];
Je l'ai fait moi-même, utilisez la catégorie ..
Source ..
@implementation NSString (VersionComparison)
- (NSComparisonResult)compareVersion:(NSString *)version{
NSArray *version1 = [self componentsSeparatedByString:@"."];
NSArray *version2 = [version componentsSeparatedByString:@"."];
for(int i = 0 ; i < version1.count || i < version2.count; i++){
NSInteger value1 = 0;
NSInteger value2 = 0;
if(i < version1.count){
value1 = [version1[i] integerValue];
}
if(i < version2.count){
value2 = [version2[i] integerValue];
}
if(value1 == value2){
continue;
}else{
if(value1 > value2){
return NSOrderedDescending;
}else{
return NSOrderedAscending;
}
}
}
return NSOrderedSame;
}
Test ..
NSString *version1 = @"3.3.1";
NSString *version2 = @"3.12.1";
NSComparisonResult result = [version1 compareVersion:version2];
switch (result) {
case NSOrderedAscending:
case NSOrderedDescending:
case NSOrderedSame:
break;
}
Je pensais que je partagerais juste une fonction que j'ai réunie pour cela. Ce n'est pas parfait du tout. Veuillez consulter les exemples et les résultats. Mais si vous vérifiez vos propres numéros de version (ce que je dois faire pour gérer des choses comme les migrations de base de données), cela peut aider un peu.
(aussi, supprimez les instructions de journal dans la méthode, bien sûr. Celles-ci sont là pour vous aider à voir ce que cela fait, c'est tout)
Tests:
[self isVersion:@"1.0" higherThan:@"0.1"];
[self isVersion:@"1.0" higherThan:@"0.9.5"];
[self isVersion:@"1.0" higherThan:@"0.9.5.1"];
[self isVersion:@"1.0.1" higherThan:@"1.0"];
[self isVersion:@"1.0.0" higherThan:@"1.0.1"];
[self isVersion:@"1.0.0" higherThan:@"1.0.0"];
// alpha tests
[self isVersion:@"1.0b" higherThan:@"1.0a"];
[self isVersion:@"1.0a" higherThan:@"1.0b"];
[self isVersion:@"1.0a" higherThan:@"1.0a"];
[self isVersion:@"1.0" higherThan:@"1.0RC1"];
[self isVersion:@"1.0.1" higherThan:@"1.0RC1"];
Résultats:
1.0 > 0.1
1.0 > 0.9.5
1.0 > 0.9.5.1
1.0.1 > 1.0
1.0.0 < 1.0.1
1.0.0 == 1.0.0
1.0b > 1.0a
1.0a < 1.0b
1.0a == 1.0a
1.0 < 1.0RC1 <-- FAILURE
1.0.1 < 1.0RC1 <-- FAILURE
notez que l'alpha fonctionne mais vous devez être très prudent avec lui. une fois que vous êtes devenu alpha à un moment donné, vous ne pouvez pas l'étendre en changeant d'autres numéros mineurs derrière.
Code:
- (BOOL) isVersion:(NSString *)thisVersionString higherThan:(NSString *)thatVersionString {
// LOWER
if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending) {
NSLog(@"%@ < %@", thisVersionString, thatVersionString);
return NO;
}
// EQUAL
if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedSame) {
NSLog(@"%@ == %@", thisVersionString, thatVersionString);
return NO;
}
NSLog(@"%@ > %@", thisVersionString, thatVersionString);
// HIGHER
return YES;
}
Ma bibliothèque iOS AppUpdateTracker contient un catégorie NSString pour effectuer ce genre de comparaison. (L'implémentation est basée sur réponse de DonnaLea .)
L'utilisation serait la suivante:
[@"1.4" isGreaterThanVersionString:@"1.3"]; // YES
[@"1.4" isLessThanOrEqualToVersionString:@"1.3"]; // NO
De plus, vous pouvez l'utiliser pour suivre l'état d'installation/mise à jour de votre application:
[AppUpdateTracker registerForAppUpdatesWithBlock:^(NSString *previousVersion, NSString *currentVersion) {
NSLog(@"app updated from: %@ to: %@", previousVersion, currentVersion);
}];
[AppUpdateTracker registerForFirstInstallWithBlock:^(NSTimeInterval installTimeSinceEpoch, NSUInteger installCount) {
NSLog(@"first install detected at: %f amount of times app was (re)installed: %lu", installTimeSinceEpoch, (unsigned long)installCount);
}];
[AppUpdateTracker registerForIncrementedUseCountWithBlock:^(NSUInteger useCount) {
NSLog(@"incremented use count to: %lu", (unsigned long)useCount);
}];
Version Swift 2.2:
let currentStoreAppVersion = "1.10.2"
let minimumAppVersionRequired = "1.2.2"
if currentStoreAppVersion.compare(minimumAppVersionRequired, options: NSStringCompareOptions.NumericSearch) ==
NSComparisonResult.OrderedDescending {
print("Current Store version is higher")
} else {
print("Latest New version is higher")
}
Version Swift 3:
let currentStoreVersion = "1.1.0.2"
let latestMinimumAppVersionRequired = "1.1.1"
if currentStoreVersion.compare(latestMinimumAppVersionRequired, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending {
print("Current version is higher")
} else {
print("Latest version is higher")
}
Voici une fonction récursive qui fait le travail avec le formatage de plusieurs versions de n'importe quelle longueur. Il fonctionne également pour @ "1.0" et @ "1.0.0"
static inline NSComparisonResult versioncmp(const NSString * a, const NSString * b)
{
if ([a isEqualToString:@""] && [b isEqualToString:@""]) {
return NSOrderedSame;
}
if ([a isEqualToString:@""]) {
a = @"0";
}
if ([b isEqualToString:@""]) {
b = @"0";
}
NSArray<NSString*> * aComponents = [a componentsSeparatedByString:@"."];
NSArray<NSString*> * bComponents = [b componentsSeparatedByString:@"."];
NSComparisonResult r = [aComponents[0] compare:bComponents[0] options:NSNumericSearch];
if(r != NSOrderedSame) {
return r;
} else {
NSString* newA = (a.length == aComponents[0].length) ? @"" : [a substringFromIndex:aComponents[0].length+1];
NSString* newB = (b.length == bComponents[0].length) ? @"" : [b substringFromIndex:bComponents[0].length+1];
return versioncmp(newA, newB);
}
}
Échantillons d'essai:
versioncmp(@"11.5", @"8.2.3");
versioncmp(@"1.5", @"8.2.3");
versioncmp(@"1.0", @"1.0.0");
versioncmp(@"11.5.3.4.1.2", @"11.5.3.4.1.2");
Pour vérifier la version dans Swift vous pouvez utiliser ce qui suit
switch newVersion.compare(currentversion, options: NSStringCompareOptions.NumericSearch) {
case .OrderedDescending:
println("NewVersion available ")
// Show Alert Here
case .OrderedAscending:
println("NewVersion Not available ")
default:
println("default")
}
J'espère que cela pourrait être utile.
Si vous savez que chaque numéro de version aura exactement 3 entiers séparés par des points, vous pouvez les analyser (par exemple en utilisant sscanf(3)
) et les comparer:
const char *version1str = "1.0.1";
const char *version2str = "1.2.5";
int major1, minor1, patch1;
int major2, minor2, patch2;
if(sscanf(version1str, "%d.%d.%d", &major1, &minor1, &patch1) == 3 &&
sscanf(version2str, "%d.%d.%d", &major2, &minor2, &patch2) == 3)
{
// Parsing succeeded, now compare the integers
if(major1 > major2 ||
(major1 == major2 && (minor1 > minor2 ||
(minor1 == minor2 && patch1 > patch2))))
{
// version1 > version2
}
else if(major1 == major2 && minor1 == minor2 && patch1 == patch2)
{
// version1 == version2
}
else
{
// version1 < version2
}
}
else
{
// Handle error, parsing failed
}
Glibc a une fonction strverscmp
et versionsort
… malheureusement, pas portable sur l'iPhone, mais vous pouvez écrire la vôtre assez facilement. Cette réimplémentation (non testée) vient de la simple lecture du comportement documenté, et non de la lecture du code source de Glibc.
int strverscmp(const char *s1, const char *s2) {
const char *b1 = s1, *b2 = s2, *e1, *e2;
long n1, n2;
size_t z1, z2;
while (*b1 && *b1 == *b2) b1++, b2++;
if (!*b1 && !*b2) return 0;
e1 = b1, e2 = b2;
while (b1 > s1 && isdigit(b1[-1])) b1--;
while (b2 > s2 && isdigit(b2[-1])) b2--;
n1 = strtol(b1, &e1, 10);
n2 = strtol(b2, &e2, 10);
if (b1 == e1 || b2 == e2) return strcmp(s1, s2);
if (n1 < n2) return -1;
if (n1 > n2) return 1;
z1 = strspn(b1, "0"), z2 = strspn(b2, "0");
if (z1 > z2) return -1;
if (z1 < z2) return 1;
return 0;
}