ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

那是 Objective-C,而不是 Swift。
一定不要混淆 CFBundleVersion & CFBundleShortVersionString`。第一个是您的 build 版本。另一个是版本号。有关详细信息,请参阅 here

A
Anye

编辑

为 Swift 4.2 更新

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

编辑

正如@azdev 在新版本的 Xcode 中指出的那样,尝试我以前的解决方案时会出现编译错误,要解决这个问题,只需按照建议编辑它,使用 !

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

@andreas infoDictionary 应该使用 ! 展开。这就是我正在使用的,放入 Globals.swift 文件中:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
我不得不再添加一个“!”在“作为”之后。 let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
我很困惑。这只是内部版本号,对吗?应用版本呢?
您应该避免使用强制展开“!”因为只要其中一个值为 nil,它们就会导致您的应用程序崩溃
@Julius我认为当这些值之一为空时应用程序应该崩溃 - 你还要做什么?
B
Blake Merryman

为 Swift 3.0 更新

NS 前缀现在在 Swift 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

旧的更新答案

自从我最初的回答以来,我一直在使用框架,所以我想将我的解决方案更新为在多捆绑环境中更简单且更有用的东西:extension NSBundle { var releaseVersionNumber: String? { return self.infoDictionary?["CFBundleShortVersionString"] as?字符串} var buildVersionNumber:字符串? { return self.infoDictionary?["CFBundleVersion"] as? String } } 现在这个扩展将在应用程序中用于识别主包和任何其他包含的包(例如用于扩展编程的共享框架或第三个框架,如 AFNetworking),如下所示:NSBundle.mainBundle().releaseVersionNumber NSBundle。 mainBundle().buildVersionNumber // 或... NSBundle(URL: someURL)?.releaseVersionNumber NSBundle(URL: someURL)?.buildVersionNumber

原始答案

我想改进一些已经发布的答案。我编写了一个类扩展,可以将其添加到您的工具链中,以便以更合乎逻辑的方式处理此问题。扩展 NSBundle { class var applicationVersionNumber: String { if let version = NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String { return version } return "Version Number Not Available" } 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。你知道要使用的新密钥吗?
Z
Zac

我知道这已经得到了回答,但我个人认为这更干净一些:

斯威夫特 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,则可选展开将导致代码被跳过。


self.labelVersion.text 是 Optional 类型,所以你可以直接赋值 NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
是否有理由不设置该值?同意 let 肯定会更加谨慎,只是想知道为什么可能需要它。谢谢!
@Crashalot 尽管你的名字;)你不希望你的应用程序崩溃,如果你打错了,而是让版本号是“出了点问题”。
OP:你可以替换 ?和 !并删除“作为字符串”。如果它为零,无论如何它不会崩溃
这就是方法。
G
Gunhan

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

(*) 为扩展更新

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

@Deprecated:旧答案

斯威夫特 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()

或者:class func getVersion() -> String { return NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as?细绳 ?? “没有版本信息”}
我认为复制其他答案没有意义。如果您的答案不再有效,那么您始终可以删除它并为其他答案腾出空间:)
@carmen_munich 既然你在这里诽谤我必须回答你。首先,这个答案是2015年3月发布的,您的答案是2017年2月发布的。因此,您的灵感必须来自较早的答案。其次,我根本没有看到你的答案,我更新了我的答案,因为我现在以这种方式使用它。我猜想,使用扩展并不是 iOS 社区中的某个人所独有的。真的,请尝试成熟并尊重其他开发人员。我在这里发帖没有任何收获。我想帮助人们就是这样。请尽量不要阻止试图帮助 SO 的人。
我从新手那里听到很多反馈,他们发布答案并希望看到有人点击“向上”,这真的很高兴看到并激励人们。但是,如果有人在他过时的答案中复制答案,那么努力发布它的人将不会获得有人投票支持的动机。所以新手们真的很失望,觉得自己没有给社区带来价值并停止发帖。不要误会,我的意思是一般来说。希望您不会感到被冒犯并现在更好地理解我提出这个建议的原因。
@carmen_munich 如果您按时间顺序排列此问题的答案,您会注意到其他人已经给出了与您之前相同的答案!所以你在责备我你自己做的事情。由于我有这个问题的历史,我在更新中分享了我的新使用偏好。就这样。
I
Iryna Batvina

对于 Swift 4.0

let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"]!
let build = Bundle.main.infoDictionary!["CFBundleVersion"]!

C
Carmen

我在 Bundle 上做了一个扩展

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))"

其实它有点不同。例如,我使用强制展开。您可能会认为强制展开通常是不好的,这是可以的罕见情况之一。为了进一步解释,这些值应该在字典中,否则项目文件确实有问题。这就是为什么这种方法使用强制展开:-)
重命名和强制展开并不是作为新答案发布的更改。此外,人们可能会在看到您的答案时了解到可以在任何地方使用强制展开。对读者来说,不要假设密钥就在那里,手动处理可选解包总是比强制解包更好。
公平地说,在极少数情况下强制展开是可以的。这篇文章只是展示了一个可以正常工作的案例。如果您想了解更多关于强制展开案例的信息,Paul Hudson 提供了一些很好的解释和教程。我真的可以向所有新手推荐 www.hackingwithswift.com
对新手来说是个不错的推荐。也许您还可以阅读更多内容并了解更多信息。此外,您应该提高对评论及其含义的理解。例如,没有人告诉你永远/曾经使用强制展开。但是对于 info dict,可以删除这些键,并且强制展开可能会导致崩溃。在这里处理展开更安全。
也许您可以解释在哪种情况下它们可以被删除并为零?我从该资源中学到的东西不是在这个特殊的情况下。除非项目文件损坏,否则它们应该始终存在,在这种情况下,项目可能无论如何都无法编译
z
ziligy

Swift 5 作为 UIApplication 扩展

extension UIApplication {
    static var release: String {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String? ?? "x.x"
    }
    static var build: String {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as! String? ?? "x"
    }
    static var version: String {
        return "\(release).\(build)"
    }
}

示例使用:

print("release: \(UIApplication.release)")
print("build: \(UIApplication.build)")
print("version: \(UIApplication.version)")

S
StrawHara

2021 年,斯威夫特 5

extension Bundle {
    public var appName: String { getInfo("CFBundleName")  }
    public var displayName: String {getInfo("CFBundleDisplayName")}
    public var language: String {getInfo("CFBundleDevelopmentRegion")}
    public var identifier: String {getInfo("CFBundleIdentifier")}
    public var copyright: String {getInfo("NSHumanReadableCopyright").replacingOccurrences(of: "\\\\n", with: "\n") }
    
    public var appBuild: String { getInfo("CFBundleVersion") }
    public var appVersionLong: String { getInfo("CFBundleShortVersionString") }
    //public var appVersionShort: String { getInfo("CFBundleShortVersion") }
    
    fileprivate func getInfo(_ str: String) -> String { infoDictionary?[str] as? String ?? "⚠️" }
}

用法(SwiftUI 示例):

    Text("Ver: \(Bundle.main.appVersionLong) (\(Bundle.main.appBuild)) ")
    
    Text(Bundle.main.copyright)
        .font(.system(size: 10, weight: .thin))
        .multilineTextAlignment(.center)

奖励:版权支持 \n 符号!

https://i.stack.imgur.com/ksGua.png


在范围内找不到 getInfo?需要导入哪个包?
@BinhHo - getInfo 是我回答的第一个代码块中编写的函数:)
z
zeeshan

Bundle+Extension.swift (SwiftUI, Swift 5, Xcode 11)

我结合了一些答案的想法,并扩展了一点:

一个 SwiftUI 示例

如果 Info.plist 中缺少该键,则显示警告三角形表情符号(而不是使应用程序崩溃)

进口基金会

extension Bundle {
    
    public var appVersionShort: String {
        if let result = infoDictionary?["CFBundleShortVersionString"] as? String {
            return result
        } else {
            return "⚠️"
        }
    }
    public var appVersionLong: String {
        if let result = infoDictionary?["CFBundleVersion"] as? String {
            return result
        } else {
            return "⚠️"
        }
    }
    public var appName: String {
        if let result = infoDictionary?["CFBundleName"] as? String {
            return result
        } else {
            return "⚠️"
        }
    }
}

SwiftUI 示例使用

VStack {

     Text("Version: \(Bundle.main.appVersionShort!) (\(Bundle.main.appVersionLong!))")
                    .font(.subheadline)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
}

我认为没有必要在 Bundle 扩展方法中返回可选字符串;常规的也适用于后备。
S
Sebastian Boldt

我为 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: " ")
    }
}

enum Constants {
    enum InfoPlist {
        static let versionNumber = "CFBundleShortVersionString"
        static let buildNumber = "CFBundleVersion"
    }
}

很好的解决方案!小的调整可能会帮助较慢/较新的开发人员。 “枚举常量”应该是“枚举标识符常量”,反之亦然。我的项目需要“导入 UIKit”。最后调用它的只是 Application.appVersion
T
Tejas

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

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

对于内部版本号,它是:

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

令人困惑的是,'CFBundleVersion' 是 Xcode 在 General->Identity 上输入的内部版本号。


H
Hugo F

Xcode 9.4.1 斯威夫特 4.1

请注意使用本地化信息字典来获取包显示名称的正确语言版本。

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
    }
}

C
Crashalot

Xcode 8,斯威夫特 3:

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

m
maslovsa

Swift 4,Bundle 的有用扩展

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 例如
z
zeeshan

捆绑+扩展.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

S
Senseful

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 ""
  }
}

C
Charlie Seligman

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

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

D
Daniel Armyr

查看文档后,我认为以下内容更清晰:

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

Source:“使用此方法优于其他访问方法,因为它会在可用时返回键的本地化值。”


那是正确的 Swift 方式,在任何地方都无需强制解包
V
VyacheslavBakinkskiy

斯威夫特 5.3

let infoDictionaryKey = kCFBundleVersionKey as String
guard let currentVersion = Bundle.main.object(forInfoDictionaryKey: infoDictionaryKey) as? String
else { fatalError("Expected to find a bundle version in the info dictionary") }

D
Dby

对于 Swift 5.0:

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

M
Michael Samoylov

对于 Swift 1.2,它是:

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

你可以用吗?以及
j
jasonnoahchoi

斯威夫特 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 我也用内部版本号更新了它。以下是所有核心基础密钥的列表:developer.apple.com/library/content/documentation/General/…
B
BadmintonCat

这是 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)"
    }
}

H
Harshil Kotecha

斯威夫特 4

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

Bundle.main.infoDictionary!["CFBundleShortVersionString"]

Swift 旧语法

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

f
futantan
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
    }

}

注意:如果没有设置应用程序版本或构建,请在此处使用if let,如果您尝试使用会导致崩溃!展开。


C
Community

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,它低于当前的内部版本号。要不向新用户显示这样的屏幕,只需在首次登录后或登录完成时添加内部版本号。


T
TheNeil

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

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

O
Olcay Ertaş
public var appVersionNumberString: String {
    get {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
    }
}

D
Davender Verma
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
            self.lblAppVersionValue.text = version
        }

u
user2807083

斯威夫特 4

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

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

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

let version = nsObject as! String