比较Swift中的两个版本字符串


73

我有两个不同的应用程序版本字符串(即“ 3.0.1”和“ 3.0.2”)。

如何使用Swift进行比较?

Answers:


139

最终不得不将我的字符串转换为NSStrings:

if storeVersion.compare(currentVersion, options: NSStringCompareOptions.NumericSearch) == NSComparisonResult.OrderedDescending {
       println("store version is newer")
}

迅捷3

let currentVersion = "3.0.1"
let storeVersion = "3.0.2"

if storeVersion.compare(currentVersion, options: .numeric) == .orderedDescending {
    print("store version is newer")
}

1
或者,您可以在swift-string上编写扩展名,这是一种非常不错的方法。
西蒙(Simon)

@Simon谢谢...我做了这样的事情:
James Laurenstin

2
extension String { func isVersionNewer(compareVersion: String) -> Bool { if self.compare(compareVersion, options: NSStringCompareOptions.NumericSearch) == NSComparisonResult.OrderedDescending { return true } return false }
詹姆斯·劳伦斯汀

2
Swift 3版本:if storeVersion.compare(currentVersion, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending { NSLog("store version is newer") }
乍得

4
重要说明:如果您决定使用.orderedSame检查版本号是否相等或较新,请注意以.0结尾的版本号。将2.4与2.4.0进行比较将在一个方向上返回true,但在另一个方向上返回false。最好的办法是避免使用以.0结尾的版本。第二个最好的事情是在比较它们可能的“ .0”版本号之前先修剪它们。
Kqtr

44

您无需将其强制转换为NSString。Swift 3中的String对象足够强大,可以比较以下版本。

let version = "1.0.0"
let targetVersion = "0.5.0"

version.compare(targetVersion, options: .numeric) == .orderedSame        // false
version.compare(targetVersion, options: .numeric) == .orderedAscending   // false
version.compare(targetVersion, options: .numeric) == .orderedDescending  // true

但是上面的示例不包含带有额外零的版本。(例如:“ 1.0.0”和“ 1.0”)

因此,我在String中制作了所有这些扩展方法,以使用Swift处理版本比较。我确实说过,它确实考虑了额外的零,非常简单,并且可以按照您的预期工作。

XCTAssertTrue(UIDevice.current.systemVersion.isVersion(lessThan: "99.0.0"))
XCTAssertTrue(UIDevice.current.systemVersion.isVersion(equalTo: UIDevice.current.systemVersion))
XCTAssertTrue(UIDevice.current.systemVersion.isVersion(greaterThan: "3.5.99"))
XCTAssertTrue(UIDevice.current.systemVersion.isVersion(lessThanOrEqualTo: "13.5.99"))
XCTAssertTrue(UIDevice.current.systemVersion.isVersion(greaterThanOrEqualTo: UIDevice.current.systemVersion))
XCTAssertTrue("0.1.1".isVersion(greaterThan: "0.1"))
XCTAssertTrue("0.1.0".isVersion(equalTo: "0.1"))
XCTAssertTrue("10.0.0".isVersion(equalTo: "10"))
XCTAssertTrue("10.0.1".isVersion(equalTo: "10.0.1"))
XCTAssertTrue("5.10.10".isVersion(lessThan: "5.11.5"))
XCTAssertTrue("1.0.0".isVersion(greaterThan: "0.99.100"))
XCTAssertTrue("0.5.3".isVersion(lessThanOrEqualTo: "1.0.0"))
XCTAssertTrue("0.5.29".isVersion(greaterThanOrEqualTo: "0.5.3"))

只需查看一下,并在我的示例扩展存储库中获取所有想要的内容,无需许可即可。

https://github.com/DragonCherry/VersionCompare


3
完美的方法,@ DragonCherry。这应该被认为是正确的答案。
莱安德罗

1
您的扩展很棒!但是,不幸的是,当版本为1.0.1.2(我知道,它应该存在,但是事情确实发生了)时,它不起作用。我更改了您的扩展名,并改进了测试以涵盖(我相信)所有情况。另外,我创建了一个扩展程序,该扩展程序从版本字符串中删除了所有不必要的字符,有时可以是v1.0.1.2。您可以在以下摘要中检查所有代码:字符串扩展名-gist.github.com/endy-s/3791fe5c856cccaabff331fd49356dbf测试-gist.github.com/endy-s/7cacaa730bf9fd5abf6021e58e962191 希望对任何人有帮助:)
Endy Silveira

1
@EndySilveira更好的主意!但是我宁愿在分别使用给定的版本方法之前应用该逻辑。当然是我个人的看法。感谢您的建议。
DragonCherry

22

Swift 3版本

let storeVersion = "3.14.10"
let currentVersion = "3.130.10"

extension String {
    func versionToInt() -> [Int] {
        return self.components(separatedBy: ".")
            .map { Int.init($0) ?? 0 }
    }
}
//true
storeVersion.versionToInt().lexicographicallyPrecedes(currentVersion.versionToInt())

Swift 2版本比较

let storeVersion = "3.14.10"

let currentVersion = "3.130.10"
extension String {
    func versionToInt() -> [Int] {
      return self.componentsSeparatedByString(".")
          .map {
              Int.init($0) ?? 0
          }
    }
}

// true
storeVersion.versionToInt().lexicographicalCompare(currentVersion.versionToInt()) 

7
如果说当前版本为“ 2.7”,并且在商店中您返回“ 2.7.0”,则此方法将无法正常工作
Pascale Beaulac

@MaudeBeaulac然后,在调用versionToInt方法之前,只需检查尾部.0字符串并将其删除。
iDevAmit

14

迅捷4+

let current = "1.3"
let appStore = "1.2.9"
let versionCompare = current.compare(appStore, options: .numeric)
if versionCompare == .orderedSame {
    print("same version")
} else if versionCompare == .orderedAscending {
    // will execute the code here
    print("ask user to update")
} else if versionCompare == .orderedDescending {
    // execute if current > appStore
    print("don't expect happen...")
}

10

以下为我工作:

extension String {

  static func ==(lhs: String, rhs: String) -> Bool {
    return lhs.compare(rhs, options: .numeric) == .orderedSame
  }

  static func <(lhs: String, rhs: String) -> Bool {
    return lhs.compare(rhs, options: .numeric) == .orderedAscending
  }

  static func <=(lhs: String, rhs: String) -> Bool {
    return lhs.compare(rhs, options: .numeric) == .orderedAscending || lhs.compare(rhs, options: .numeric) == .orderedSame
  }

  static func >(lhs: String, rhs: String) -> Bool {
    return lhs.compare(rhs, options: .numeric) == .orderedDescending
  }

  static func >=(lhs: String, rhs: String) -> Bool {
    return lhs.compare(rhs, options: .numeric) == .orderedDescending || lhs.compare(rhs, options: .numeric) == .orderedSame
  }

}


"1.2.3" == "1.2.3" // true
"1.2.3" > "1.2.3" // false
"1.2.3" >= "1.2.3" // true
"1.2.3" < "1.2.3" // false
"1.2.3" <= "1.2.3" // true

"3.0.0" >= "3.0.0.1" // false
"3.0.0" > "3.0.0.1" // false
"3.0.0" <= "3.0.0.1" // true
"3.0.0.1" >= "3.0.0.1" // true
"3.0.1.1.1.1" >= "3.0.2" // false
"3.0.15" > "3.0.1.1.1.1" // true
"3.0.10" > "3.0.100.1.1.1" // false
"3.0.1.1.1.3.1.7" == "3.0.1.1.1.3.1" // false
"3.0.1.1.1.3.1.7" > "3.0.1.1.1.3.1" // true

"3.14.10" == "3.130.10" // false
"3.14.10" > "3.130.10" // false
"3.14.10" >= "3.130.10" // false
"3.14.10" < "3.130.10" // true
"3.14.10" <= "3.130.10" // true

在此处输入图片说明


1
这种方法的问题是“ 1.0” <“ 1.0.0”将为真。DragonCherry的方法没有这个问题。
ThomasW

3

有时,storeVersion的长度不等于currentVersion的长度。例如,也许storeVersion是3.0.0,但是,您修复了一个错误并将其命名为3.0.0.1

func ascendingOrSameVersion(minorVersion smallerVersion:String, largerVersion:String)->Bool{
    var result = true //default value is equal

    let smaller = split(smallerVersion){ $0 == "." }
    let larger = split(largerVersion){ $0 == "." }

    let maxLength = max(smaller.count, larger.count)

    for var i:Int = 0; i < maxLength; i++ {
        var s = i < smaller.count ? smaller[i] : "0"
        var l = i < larger.count ? larger[i] : "0"
        if s != l {
            result = s < l
            break
        }
    }
    return result
}

该版本最好为3.0.1。
chengsam

2

使用Swift 3,可以使用带有设置为数字选项的比较功能来比较应用程序版本字符串。

阅读Apple开发人员文档中的此String编程指南,以获取有关其工作原理的Objective-C示例。

我在https://iswift.org/playground尝试了这些

print("2.0.3".compare("2.0.4", options: .numeric))//orderedAscending
print("2.0.3".compare("2.0.5", options: .numeric))//orderedAscending

print("2.0.4".compare("2.0.4", options: .numeric))//orderedSame

print("2.0.4".compare("2.0.3", options: .numeric))//orderedDescending
print("2.0.5".compare("2.0.3", options: .numeric))//orderedDescending

print("2.0.10".compare("2.0.11", options: .numeric))//orderedAscending
print("2.0.10".compare("2.0.20", options: .numeric))//orderedAscending

print("2.0.0".compare("2.0.00", options: .numeric))//orderedSame
print("2.0.00".compare("2.0.0", options: .numeric))//orderedSame

print("2.0.20".compare("2.0.19", options: .numeric))//orderedDescending
print("2.0.99".compare("2.1.0", options: .numeric))//orderedAscending

希望有帮助!

如果您喜欢使用库,请使用此库,不要重新发明轮子。 https://github.com/zenangst/Versions


2

您可以使用“ String.compare”方法进行操作。使用CompareResult来确定您的版本何时大于,等于或小于。

例:

"1.1.2".compare("1.1.1").rawValue -> ComparisonResult.orderedDescending
"1.1.2".compare("1.1.2").rawValue -> ComparisonResult.orderedSame
"1.1.2".compare("1.1.3").rawValue -> ComparisonResult.orderedAscending

2

写了一个小的Swift 3类来做到这一点:

class VersionString: NSObject {

    // MARK: - Properties
    var string = ""
    override var description: String {
        return string
    }

    // MARK: - Initialization
    private override init() {
        super.init()
    }
    convenience init(_ string: String) {
        self.init()
        self.string = string
    }

    func compare(_ rhs: VersionString) -> ComparisonResult {
        return string.compare(rhs.string, options: .numeric)
    }

    static func ==(lhs: VersionString, rhs: VersionString) -> Bool {
        return lhs.compare(rhs) == .orderedSame
    }

    static func <(lhs: VersionString, rhs: VersionString) -> Bool {
        return lhs.compare(rhs) == .orderedAscending
    }

    static func <=(lhs: VersionString, rhs: VersionString) -> Bool {
        return lhs.compare(rhs) == .orderedAscending || lhs.compare(rhs) == .orderedSame
    }

    static func >(lhs: VersionString, rhs: VersionString) -> Bool {
        return lhs.compare(rhs) == .orderedDescending
    }

    static func >=(lhs: VersionString, rhs: VersionString) -> Bool {
        return lhs.compare(rhs) == .orderedDescending || lhs.compare(rhs) == .orderedSame
    }
}

let v1 = VersionString("1.2.3")
let v2 = VersionString("1.2.3")

print("\(v1) == \(v2): \(v1 == v2)") // 1.2.3 == 1.2.3: true
print("\(v1) >  \(v2): \(v1 > v2)")  // 1.2.3 >  1.2.3: false
print("\(v1) >= \(v2): \(v1 >= v2)") // 1.2.3 >= 1.2.3: true
print("\(v1) <  \(v2): \(v1 < v2)")  // 1.2.3 <  1.2.3: false
print("\(v1) <= \(v2): \(v1 <= v2)") // 1.2.3 <= 1.2.3: true

2

@DragonCherry解决方案很棒!

但是,不幸的是,当版本为1.0.1.2(我知道,它不应该存在,但是事情确实发生了)时,它不起作用。

我更改了您的扩展名,并改进了测试以涵盖(我相信)所有情况。另外,我创建了一个扩展程序,该扩展程序从版本字符串中删除了所有不必要的字符,有时可以是v1.0.1.2

您可以检查以下要点中的所有代码:

希望它可以帮助任何人:)


2

我将Ashok版本和DragonCherry混合使用:

// MARK: - Version comparison

extension String {

    // Modified from the DragonCherry extension - https://github.com/DragonCherry/VersionCompare
    private func compare(toVersion targetVersion: String) -> ComparisonResult {
        let versionDelimiter = "."
        var result: ComparisonResult = .orderedSame
        var versionComponents = components(separatedBy: versionDelimiter)
        var targetComponents = targetVersion.components(separatedBy: versionDelimiter)

        while versionComponents.count < targetComponents.count {
            versionComponents.append("0")
        }

        while targetComponents.count < versionComponents.count {
            targetComponents.append("0")
        }

        for (version, target) in zip(versionComponents, targetComponents) {
            result = version.compare(target, options: .numeric)
            if result != .orderedSame {
                break
            }
        }

        return result
    }

    func isVersion(equalTo targetVersion: String) -> Bool { return compare(toVersion: targetVersion) == .orderedSame }

    func isVersion(greaterThan targetVersion: String) -> Bool { return compare(toVersion: targetVersion) == .orderedDescending }

    func isVersion(greaterThanOrEqualTo targetVersion: String) -> Bool { return compare(toVersion: targetVersion) != .orderedAscending }

    func isVersion(lessThan targetVersion: String) -> Bool { return compare(toVersion: targetVersion) == .orderedAscending }

    func isVersion(lessThanOrEqualTo targetVersion: String) -> Bool { return compare(toVersion: targetVersion) != .orderedDescending }

    static func ==(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) == .orderedSame }

    static func <(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) == .orderedAscending }

    static func <=(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) != .orderedDescending }

    static func >(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) == .orderedDescending }

    static func >=(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) != .orderedAscending }

}

使用:

"1.2.3" == "1.2.3" // true
"1.2.3" > "1.2.3" // false
"1.2.3" >= "1.2.3" // true
"1.2.3" < "1.2.3" // false
"1.2.3" <= "1.2.3" // true

"3.0.0" >= "3.0.0.1" // false
"3.0.0" > "3.0.0.1" // false
"3.0.0" <= "3.0.0.1" // true
"3.0.0.1" >= "3.0.0.1" // true
"3.0.1.1.1.1" >= "3.0.2" // false
"3.0.15" > "3.0.1.1.1.1" // true
"3.0.10" > "3.0.100.1.1.1" // false
"3.0.1.1.1.3.1.7" == "3.0.1.1.1.3.1" // false
"3.0.1.1.1.3.1.7" > "3.0.1.1.1.3.1" // true

"3.14.10" == "3.130.10" // false
"3.14.10" > "3.130.10" // false
"3.14.10" >= "3.130.10" // false
"3.14.10" < "3.130.10" // true
"3.14.10" <= "3.130.10" // true

"0.1.1".isVersion(greaterThan: "0.1")
"0.1.0".isVersion(equalTo: "0.1")
"10.0.0".isVersion(equalTo: "10")
"10.0.1".isVersion(equalTo: "10.0.1")
"5.10.10".isVersion(lessThan: "5.11.5")
"1.0.0".isVersion(greaterThan: "0.99.100")
"0.5.3".isVersion(lessThanOrEqualTo: "1.0.0")
"0.5.29".isVersion(greaterThanOrEqualTo: "0.5.3")

1

NSString的正确使用方法是正确的,但是这是一项非基金会的尝试:

let storeVersion = "3.14.10"

let currentVersion = "3.130.10"

func versionToArray(version: String) -> [Int] {
    return split(version) {
        $0 == "."
    }.map {
        // possibly smarter ways to do this
        $0.toInt() ?? 0
    }
}

storeVersion < currentVersion  // false

// true
lexicographicalCompare(versionToArray(storeVersion), versionToArray(currentVersion))

这是一个不错的快速解决方案!它可以与Swift 2一起使用吗?
Natan R.

@NatanR。空速Velocity代码似乎适用于Swift1。有关Swift 2和3+的等效内容,请参见Wane答案。
心教堂

0

这是一个简单的快速结构

public struct VersionString: Comparable {

    public let rawValue: String

    public init(_ rawValue: String) {
        self.rawValue = rawValue
    }

    public static func == (lhs: VersionString, rhs: VersionString) -> Bool {
        return lhs.rawValue.compare(rhs.rawValue, options: .numeric) == .orderedSame
    }

    public static func < (lhs: VersionString, rhs: VersionString) -> Bool {
        return lhs.rawValue.compare(rhs.rawValue, options: .numeric) == .orderedAscending
    }
}

0

这个怎么样:

class func compareVersion(_ ver: String, to toVer: String) -> ComparisonResult {
    var ary0 = ver.components(separatedBy: ".").map({ return Int($0) ?? 0 })
    var ary1 = toVer.components(separatedBy: ".").map({ return Int($0) ?? 0 })
    while ary0.count < 3 {
        ary0.append(0)
    }
    while ary1.count < 3 {
        ary1.append(0)
    }
    let des0 = ary0[0...2].description
    let des1 = ary1[0...2].description
    return des0.compare(des1, options: .numeric)
}

并测试:

func test_compare_version() {
    XCTAssertEqual(compareVersion("1.0.0", to: "1.0.0"), .orderedSame)
    XCTAssertEqual(compareVersion("1.0.0", to: "1.0."), .orderedSame)
    XCTAssertEqual(compareVersion("1.0.0", to: "1.0"), .orderedSame)
    XCTAssertEqual(compareVersion("1.0.0", to: "1."), .orderedSame)
    XCTAssertEqual(compareVersion("1.0.0", to: "1"), .orderedSame)
    XCTAssertEqual(compareVersion("1.0.0", to: "1.0.1"), .orderedAscending)
    XCTAssertEqual(compareVersion("1.0.0", to: "1.1."), .orderedAscending)
    XCTAssertEqual(compareVersion("1.0.0", to: "1.1"), .orderedAscending)
    XCTAssertEqual(compareVersion("1.0.0", to: "2."), .orderedAscending)
    XCTAssertEqual(compareVersion("1.0.0", to: "2"), .orderedAscending)

    XCTAssertEqual(compareVersion("1.0.0", to: "1.0.0"), .orderedSame)
    XCTAssertEqual(compareVersion("1.0.", to: "1.0.0"), .orderedSame)
    XCTAssertEqual(compareVersion("1.0", to: "1.0.0"), .orderedSame)
    XCTAssertEqual(compareVersion("1.", to: "1.0.0"), .orderedSame)
    XCTAssertEqual(compareVersion("1", to: "1.0.0"), .orderedSame)
    XCTAssertEqual(compareVersion("1.0.1", to: "1.0.0"), .orderedDescending)
    XCTAssertEqual(compareVersion("1.1.", to: "1.0.0"), .orderedDescending)
    XCTAssertEqual(compareVersion("1.1", to: "1.0.0"), .orderedDescending)
    XCTAssertEqual(compareVersion("2.", to: "1.0.0"), .orderedDescending)
    XCTAssertEqual(compareVersion("2", to: "1.0.0"), .orderedDescending)

    XCTAssertEqual(compareVersion("1.0.0", to: "0.9.9"), .orderedDescending)
    XCTAssertEqual(compareVersion("1.0.0", to: "0.9."), .orderedDescending)
    XCTAssertEqual(compareVersion("1.0.0", to: "0.9"), .orderedDescending)
    XCTAssertEqual(compareVersion("1.0.0", to: "0."), .orderedDescending)
    XCTAssertEqual(compareVersion("1.0.0", to: "0"), .orderedDescending)

    XCTAssertEqual(compareVersion("", to: "0"), .orderedSame)
    XCTAssertEqual(compareVersion("0", to: ""), .orderedSame)
    XCTAssertEqual(compareVersion("", to: "1"), .orderedAscending)
    XCTAssertEqual(compareVersion("1", to: ""), .orderedDescending)

    XCTAssertEqual(compareVersion("1.0.0", to: "1.0.0.9"), .orderedSame)
    XCTAssertEqual(compareVersion("1.0.0.9", to: "1.0.0"), .orderedSame)
}

0

我可以理解,这里给出了许多很好的答案。这是我对应用程序版本进行比较的版本。

func compareVersions(installVersion: String, storeVersion: String) -> Bool{
        // 1.7.5
        var installedArr = installVersion.components(separatedBy: ".")
        var storeArr = storeVersion.components(separatedBy: ".")
        var isAvailable = false

        while(installedArr.count < storeArr.count){
            installedArr.append("0")
        }

        while(storeArr.count < installedArr.count){
            storeArr.append("0")
        }

        for index in 0 ..< installedArr.count{
            if let storeIndex=storeArr[index].toInt(), let installIndex=installedArr[index].toInt(){
                if storeIndex > installIndex{
                    isAvailable = true
                    return isAvailable
                }
            }
        }

        return isAvailable
    }

0

这是我的工作,涵盖了所有版本格式的情况,例如compare"10.0""10.0.1",让我知道是否错过任何情况。

这是要点https://gist.github.com/shamzahasan88/bc22af2b7c96b6a06a064243a02c8bcc。希望对大家有帮助。

如果有人不想对我的要点进行评分,则这里是代码:P

extension String {

  // Version format "12.34.39" where "12" is "Major", "34" is "Minor" and "39" is "Bug fixes"
  // "maximumDigitCountInVersionComponent" is optional parameter determines the maximum length of "Maajor", "Minor" and "Bug Fixes" 
  func shouldUpdateAsCompareToVersion(storeVersion: String, maximumDigitCountInVersionComponent: Int = 5) -> Bool {
        let adjustTralingZeros: (String, Int)->String = { val, count in
            return "\(val)\((0..<(count)).map{_ in "0"}.joined(separator: ""))"
        }
        let adjustLength: ([String.SubSequence], Int)->[String] = { strArray, count in
            return strArray.map {adjustTralingZeros("\($0)", count-$0.count)}
        }
        let storeVersionSubSequence = storeVersion.split(separator: ".")
        let currentVersionSubSequence = self.split(separator: ".")
        let formatter = NumberFormatter()
        formatter.minimumIntegerDigits = maximumDigitCountInVersionComponent
        formatter.maximumIntegerDigits = maximumDigitCountInVersionComponent
        let storeVersions = adjustLength(storeVersionSubSequence, maximumDigitCountInVersionComponent)
        let currentVersions = adjustLength(currentVersionSubSequence, maximumDigitCountInVersionComponent)
        var storeVersionString = storeVersions.joined(separator: "")
        var currentVersionString = currentVersions.joined(separator: "")
        let diff = storeVersionString.count - currentVersionString.count

        if diff > 0 {
            currentVersionString = adjustTralingZeros(currentVersionString, diff)
        }else if diff < 0 {
            storeVersionString = adjustTralingZeros(storeVersionString, -diff)
        }
        let literalStoreVersion = Int(storeVersionString)!
        let literalCurrentVersion = Int(currentVersionString)!
        return literalCurrentVersion < literalStoreVersion
    }
}

用法:

print("33.29".shouldUpdateAsCompareToVersion(storeVersion: "34.23.19")) // true
print("35.29".shouldUpdateAsCompareToVersion(storeVersion: "34.23.19")) // false
print("34.23.2".shouldUpdateAsCompareToVersion(storeVersion: "34.23.19")) // false
print("34.23.18".shouldUpdateAsCompareToVersion(storeVersion: "34.23.19")) // true

0
extension String {

    func compareVersionNumbers(other: String) -> Int {
        
        let nums1 = self.split(separator: ".").map({ Int($0) ?? 0 })
        let nums2 = other.split(separator: ".").map({ Int($0) ?? 0 })
        
        for i in 0..<max(nums1.count, nums2.count) {
            
            let num1 = i < nums1.count ? nums1[i] : 0
            let num2 = i < nums2.count ? nums2[i] : 0
            
            if num1 != num2 {
                return num1 - num2
            }
        }
        
        return 0
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.