如何使用Swift获取App版本和内部版本号?


386

我有一个带有Azure后端的IOS应用程序,并且想记录某些事件,例如登录名和正在运行的应用程序版本。

如何使用Swift返回版本和内部版本号?



6
那是Objective-C,而不是Swift。
岛之风维克

10
确保不要混淆CFBundleVersion&CFBundleShortVersionString`。首先是您的构建版本。另一个是版本号。看到这里更多信息
亲爱的

1
@ØyvindVik大多数人认为您无需手持即可将Objective-C解决方案转换为Swift。
gnasher729

Answers:


424

编辑

为Swift 4.2更新

let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String

编辑

正如@azdev在新版本的Xcode上指出的那样,尝试我以前的解决方案时会遇到编译错误,要解决此问题,只需按照建议的方法对其进行编辑即可使用!来解开bundle字典。

let nsObject: AnyObject? = Bundle.main.infoDictionary!["CFBundleShortVersionString"]

结束编辑

只需使用与Objective-C中相同的逻辑,但有一些小的更改

//First get the nsObject by defining as an optional anyObject
let nsObject: AnyObject? = NSBundle.mainBundle().infoDictionary["CFBundleShortVersionString"]

//Then just cast the object as a String, but be careful, you may want to double check for nil
let version = nsObject as! String

我希望这能够帮到你。

大卫


当我使用它时,我得到一个编译器错误“ [NSObject:AnyObject]?没有名为'subscript'的成员”
andreas 2014年

您可以尝试替换AnyObject吗?通过AnyObject!在不确切知道您使用的代码的情况下,很难猜测出什么问题,还请记住,Swift可以从一个Xcode版本更改为另一个版本,因此了解您的Xcode版本也很重要。
大卫

18
@andreas infoDictionary应该使用展开!。这是我正在使用的内容,放置在Globals.swift文件中:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
azdev 2014年

1
我必须再添加一个“!” 在“ as”之后。let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
yosei 2015年

3
您应该避免使用强制拆开“!” 因为这些值之一为nil时,它们都会导致您的应用程序崩溃
Julius

278

我知道已经回答了这个问题,但就我个人而言,我认为这更干净一些:

Swift 3.0:

 if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
    self.labelVersion.text = version
}

迅捷<2.3

if let version = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String {
    self.labelVersion.text = version
}

这样,if let版本负责条件处理(在我的情况下设置标签文本),并且如果infoDictionary或CFBundleShortVersionString为nil,则可选的展开将导致代码被跳过。


6
self.labelVersion.text是可选类型,因此您可以直接分配NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
Jonauz 2015年

8
是否有理由不设置该值?同意肯定要谨慎一点let,只是想知道为什么会需要它。谢谢!
Crashalot

@Crashalot尽管有您的名字;)例如,如果您输入错误,您不希望您的应用崩溃,而是让版本号为“出问题了”。
丹尼尔·斯普林格'18

OP:您可以更换?用!并删除“ as String”。如果它为零,那么无论如何都不会崩溃
Daniel Springer

245

为Swift 3.0更新

NS现在-prefixes都走了斯威夫特3.0和几个属性/方法已经改变名称,以更加SWIFTY。这是现在的样子:

extension Bundle {
    var releaseVersionNumber: String? {
        return infoDictionary?["CFBundleShortVersionString"] as? String
    }
    var buildVersionNumber: String? {
        return infoDictionary?["CFBundleVersion"] as? String
    }
}

Bundle.main.releaseVersionNumber
Bundle.main.buildVersionNumber

旧的更新答案

自从我最初的回答以来,我一直在使用Frameworks,所以我想将我的解决方案更新为在多捆绑环境中既简单又有用的解决方案:

extension NSBundle {

    var releaseVersionNumber: String? {
        return self.infoDictionary?["CFBundleShortVersionString"] as? String
    }

    var buildVersionNumber: String? {
        return self.infoDictionary?["CFBundleVersion"] as? String
    }

}

现在,此扩展将在应用程序中有用,以识别主捆绑包和任何其他包含的捆绑包(例如,用于扩展编程的共享框架或诸如AFNetworking的第三种框架),如下所示:

NSBundle.mainBundle().releaseVersionNumber
NSBundle.mainBundle().buildVersionNumber

// or...

NSBundle(URL: someURL)?.releaseVersionNumber
NSBundle(URL: someURL)?.buildVersionNumber

原始答案

我想改善一些已经发布的答案。我写了一个类扩展,可以将其添加到您的工具链中,以更合乎逻辑的方式进行处理。

extension NSBundle {

class var applicationVersionNumber: String {
    if let version = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"]

如?字符串{返回版本}返回“版本号不可用”}

class var applicationBuildNumber: String {
    if let build = NSBundle.mainBundle().infoDictionary?["CFBundleVersion"] as? String {
        return build
    }
    return "Build Number Not Available"
}

}

现在,您可以通过以下方式轻松访问此地址:

let versionNumber = NSBundle.applicationVersionNumber

CFBundleVersionKey在Swift 3,Xcode 8中不再起作用。您知道要使用的新密钥吗?
Crashalot

71

我也知道这已经得到回答,但是我总结了以前的答案:

(*)已更新扩展名

extension Bundle {
    var releaseVersionNumber: String? {
        return infoDictionary?["CFBundleShortVersionString"] as? String
    }
    var buildVersionNumber: String? {
        return infoDictionary?["CFBundleVersion"] as? String
    }
    var releaseVersionNumberPretty: String {
        return "v\(releaseVersionNumber ?? "1.0.0")"
    }
}

用法:

someLabel.text = Bundle.main.releaseVersionNumberPretty

@不推荐使用:旧答案

Swift 3.1

class func getVersion() -> String {
    guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
        return "no version info"
    }
    return version
}

对于旧版本

class func getVersion() -> String {
    if let version = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String {
        return version
    }
    return "no version info"
}

因此,如果您要设置标签文本或要在其他地方使用,请执行以下操作:

self.labelVersion.text = getVersion()

1
或:class func getVersion()->字符串{返回NSBundle.mainBundle()。infoDictionary?[“ CFBundleShortVersionString”]为?字符串?? “没有版本信息”}
tapmonkey

我认为复制其他答案没有任何意义。如果您的答案不再有效,那么您始终可以将其删除并为其他答案腾出空间:)
carmen_munich

1
@carmen_munich因为您在这里诽谤,所以我必须回答您。首先,此答案发布于2015年3月,您的答案发布于2017年2月。因此,您的灵感必须来自较早的答案。其次,我根本看不到您的答案,我更新了我的答案,因为如今我是这样使用的。我想,使用扩展并不是iOS社区中某人独有的。确实,请尝试成熟并尊重其他开发人员。我在这里什么都没得到。我想帮助人们。请不要劝阻那些试图在SO方面提供帮助的人。
Gunhan

我收到了很多新手的反馈,他们发布了答案并希望看到某人单击“向上”,这真是很高兴看到并激励人们。但是,如果有人在过时的答案中复制答案,那么努力发表该答案的人将不会获得有人投票支持的动机。因此,新手确实感到很失望,并感到他们没有为社区带来价值并停止发布。别误会,我的意思是总的来说。希望您不要感到生气,现在更好地理解我为什么提出这个建议。
carmen_munich

@carmen_munich如果您按时间顺序排列该问题的答案,您会发现其他人已经给出了与您之前相同的答案!所以你要怪我自己做的事情。由于我有此问题的历史记录,因此我在更新中分享了新的使用偏好。就这样。
Gunhan


28

我做了捆绑扩展

extension Bundle {

    var appName: String {
        return infoDictionary?["CFBundleName"] as! String
    }

    var bundleId: String {
        return bundleIdentifier!
    }

    var versionNumber: String {
        return infoDictionary?["CFBundleShortVersionString"] as! String 
    }

    var buildNumber: String {
        return infoDictionary?["CFBundleVersion"] as! String
    }

}

然后用它

versionLabel.text = "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))"

其实有点不同。例如,我使用强制展开。您可能会认为一般而言,解开力是不好的,这是少数可以解决的情况之一。为了进一步解释,这些值应该在字典中,否则项目文件确实有问题。这就是为什么这种方法使用力解包的原因:-)
carmen_munich '19

重命名和强制展开不会更改为发布新答案。另外,人们可能会发现,只要看到您的回答,便可以在任何地方使用强制展开。对读者来说,不要以为钥匙在那里,最好手动处理可选的展开而不是强制展开。
Gunhan

公平地说,在一些罕见的情况下,可以进行力解开。这篇文章只是显示了一种可以解决的情况。如果您想更多地了解力解开案例,Paul Hudson会提供一些很好的解释和教程。我真的可以向所有新手推荐www.hackingwithswift.com
carmen_munich 19-4-24

对于新手来说,这是一个很好的建议。也许您还可以阅读更多信息并了解更多信息。另外,您还应该提高对注释及其含义的理解。例如,没有人告诉您永远不要使用武力解包。但是对于信息命令来说,可以将这些键移开,而用力拉开可能会导致崩溃。在这里处理展开更安全。
Gunhan

也许您可以解释在什么情况下它们可以被删除并为零?我从参考资料中学到的内容不是在这种特殊情况下。除非项目文件被破坏,否则它们应该一直存在,否则项目可能就无法编译
carmen_munich 19-4-24

16

对于Swift 3.0 NSBundle不起作用,以下代码可完美工作。

let versionNumberString =
      Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")
          as! String

对于内部版本号,它是:

let buildNumberString =
      Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion")
          as! String

令人困惑的是,“ CFBundleVersion”是在“常规”- >“身份”上的Xcode中输入的内部版本号。


15

Xcode 9.4.1 Swift 4.1

请注意,使用localizedInfoDictionary来选择包显示名称的正确语言版本。

var displayName: String?
var version: String?
var build: String?

override func viewDidLoad() {
    super.viewDidLoad()

    // Get display name, version and build

    if let displayName = Bundle.main.localizedInfoDictionary?["CFBundleDisplayName"] as? String {
        self.displayName = displayName
    }
    if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
        self.version = version
    }
    if let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
        self.build = build
    }
}

14

Xcode 8,Swift 3:

let gAppVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") ?? "0"
let gAppBuild = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") ?? "0"

12

Swift 4,有用的捆绑扩展

import Foundation

public extension Bundle {

    public var shortVersion: String {
        if let result = infoDictionary?["CFBundleShortVersionString"] as? String {
            return result
        } else {
            assert(false)
            return ""
        }
    }

    public var buildVersion: String {
        if let result = infoDictionary?["CFBundleVersion"] as? String {
            return result
        } else {
            assert(false)
            return ""
        }
    }

    public var fullVersion: String {
        return "\(shortVersion)(\(buildVersion))"
    }
}

要使用此功能,您需要说出Bundle.main.fullVersion例如
Joseph Astrahan

11

Bundle + Extensions.swift

import Foundation

extension Bundle {
    var versionNumber: String? {
        return infoDictionary?["CFBundleShortVersionString"] as? String
    }

    var buildNumber: String? {
        return infoDictionary?["CFBundleVersion"] as? String
    }

    var bundleName: String? {
        return infoDictionary?["CFBundleName"] as? String
    }
}

用法:

someLabel.text = Bundle.main.versionNumber

11

OP要求提供版本号和内部版本号。不幸的是,大多数答案都没有提供这两个选项。此外,其他人添加了不必要的扩展方法。这是一个非常简单的解决OP问题的方法:

// Example output: "1.0 (234)"
private func versionAndBuildNumber() -> String {
  let versionNumber = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
  let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String
  if let versionNumber = versionNumber, let buildNumber = buildNumber {
    return "\(versionNumber) (\(buildNumber))"
  } else if let versionNumber = versionNumber {
    return versionNumber
  } else if let buildNumber = buildNumber {
    return buildNumber
  } else {
    return ""
  }
}

9

鉴于Swift不断发展,我的回答(截至2015年8月):

let version = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String

8

我为UIApplication创建了一个扩展。

extension UIApplication {
    static var appVersion: String {
        let versionNumber = Bundle.main.infoDictionary?[IdentifierConstants.InfoPlist.versionNumber] as? String
        let buildNumber = Bundle.main.infoDictionary?[IdentifierConstants.InfoPlist.buildNumber] as? String

        let formattedBuildNumber = buildNumber.map {
            return "(\($0))"
        }

        return [versionNumber,formattedBuildNumber].compactMap { $0 }.joined(separator: " ")
    }
}

struct IdentifierConstants {
    struct InfoPlist {
        static let versionNumber = "CFBundleShortVersionString"
        static let buildNumber = "CFBundleVersion"
    }
}

6

看完文档,我相信以下内容更干净:

let version = 
NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") 
as? String

来源:“与其他访问方法相比,首选使用此方法,因为它会在密钥可用时返回密钥的本地化值。”


1
多数民众赞成在正确的Swift方式,没有力量解包,任何地方
Juan Boero


4

斯威夫特3:

版本号

if let versionNumberString = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { // do something }

内部编号

if let buildNumberString = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { // do something }

内部编号怎么样?谢谢!CFBundleVersionKey不起作用。
Crashalot

@Crashalot我也用内部版本号更新了它。这也是所有核心基础键的列表:developer.apple.com/library/content/documentation/General/...
jasonnoahchoi

3

斯威夫特4

func getAppVersion() -> String {
    return "\(Bundle.main.infoDictionary!["CFBundleShortVersionString"] ?? "")"
}

Bundle.main.infoDictionary![“ CFBundleShortVersionString”]

迅捷的旧语法

let appVer: AnyObject? = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"]

2
extension UIApplication {

    static var appVersion: String {
        if let appVersion = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") {
            return "\(appVersion)"
        } else {
            return ""
        }
    }

    static var build: String {
        if let buildVersion = NSBundle.mainBundle().objectForInfoDictionaryKey(kCFBundleVersionKey as String) {
            return "\(buildVersion)"
        } else {
            return ""
        }
    }

    static var versionBuild: String {
        let version = UIApplication.appVersion
        let build = UIApplication.build

        var versionAndBuild = "v\(version)"

        if version != build {
            versionAndBuild = "v\(version)(\(build))"
        }

        return versionAndBuild
    }

}

注意:如果未设置应用程序版本或内部版本,则应在此处使用,如果尝试使用,将导致崩溃!打开。


2

这是Swift 3.2的更新版本:

extension UIApplication
{
    static var appVersion:String
    {
        if let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")
        {
            return "\(appVersion)"
        }
        return ""
    }

    static var buildNumber:String
    {
        if let buildNum = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String)
        {
            return "\(buildNum)"
        }
        return ""
    }

    static var versionString:String
    {
        return "\(appVersion).\(buildNumber)"
    }
}

2

您现在可以为此使用常量,而不必像以前那样使用字符串类型的代码,这使事情变得更加方便。

var appVersion: String {
    return Bundle.main.infoDictionary![kCFBundleVersionKey as String] as! String
}


1

SWIFT 4

//首先通过定义为可选的AnyObject获得nsObject

let nsObject: AnyObject? = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as AnyObject

//然后将对象强制转换为字符串,但是要小心,您可能需要仔细检查nil

let version = nsObject as! String

1

简单的实用程序功能可将App版本返回为Int

func getAppVersion() -> Int {

        if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {

            let appVersionClean = appVersion.replacingOccurrences(of: ".", with: "", options: NSString.CompareOptions.literal, range:nil)

            if let appVersionNum = Int(appVersionClean) {
                return appVersionNum
            }
        }
        return 0
    }


0

对于任何感兴趣的人,SwifterSwiftgithub上都有一个不错的,整洁的库,该库也为swift的每个版本提供了完整的文档(请参阅swifterswift.com)。

使用此库,读取应用程序版本和内部版本号将像下面这样简单:

import SwifterSwift

let buildNumber = SwifterSwift.appBuild
let version = SwifterSwift.appVersion


0

Swift 5更新

这是我用来决定是否显示“应用程序更新”页面的功能。它返回内部版本号,我将其转换为Int:

if let version: String = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
        guard let intVersion = Int(version) else { return }

        if UserDefaults.standard.integer(forKey: "lastVersion") < intVersion {
            print("need to show popup")
        } else {
            print("Don't need to show popup")
        }

        UserDefaults.standard.set(intVersion, forKey: "lastVersion")
    }

如果之前从未使用过,它将返回比当前内部版本号低的0。要不向新用户显示此屏幕,只需在首次登录后或完成注册后添加内部版本号。


0

对于Swift 5.0

let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String

-1

对于Swift 2.0

//First get the nsObject by defining as an optional anyObject

let nsObject: AnyObject? = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"]
let version = nsObject as! String
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.