Comment obtenir la version de l'application et le numéro de build à l'aide de Swift?

388

J'ai une application IOS avec un back-end Azure et je souhaite enregistrer certains événements, comme les connexions et les versions des utilisateurs de l'application qui s'exécutent.

Comment puis-je retourner la version et le numéro de build à l'aide de Swift?

Øyvind Vik
la source
6
C'est Objective-C, pas Swift.
Øyvind Vik
10
Assurez-vous de ne pas confondre CFBundleVersion& CFBundleShortVersionString`. Le premier est votre version de build . L'autre est le numéro de version . Voir ici pour plus d'informations
Honey
1
@ ØyvindVik La plupart des gens pensent que vous pouvez traduire une solution Objective-C en Swift sans tenir la main.
gnasher729

Réponses:

425

ÉDITER

Mis à jour pour Swift 4.2

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

ÉDITER

Comme l'a souligné @azdev sur la nouvelle version de Xcode, vous obtiendrez une erreur de compilation pour essayer ma solution précédente, pour résoudre ce problème, il suffit de la modifier comme suggéré pour déballer le dictionnaire de bundle en utilisant un!

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

Fin de la modification

Utilisez simplement la même logique que dans Objective-C mais avec quelques petits changements

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

J'espère que cela vous aide.

David

David
la source
Lorsque j'utilise ceci, j'obtiens une erreur de compilation "[NSObject: AnyObject]? N'a pas de membre nommé 'indice'"
andreas
Vous pouvez essayer de remplacer AnyObject? par AnyObject! sans savoir exactement le code que vous utilisez, il est très difficile de deviner ce qui ne va pas, gardez également à l'esprit que Swift peut changer d'une version de Xcode à une autre, il sera donc également pertinent de connaître votre version de Xcode.
David
18
@andreas infoDictionarydoit être déballé en utilisant !. Voici ce que j'utilise, placé dans un fichier Globals.swift:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
azdev
1
J'ai dû en ajouter un de plus "!" après "comme". let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
yosei
3
Vous devez éviter d'utiliser un déballage forcé "!" car ils entraîneront le blocage de votre application chaque fois que l'une de ces valeurs est nulle
Julius
279

Je sais que cela a déjà été répondu, mais personnellement, je pense que c'est un peu plus propre:

Swift 3.0:

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

Rapide <2,3

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

De cette façon, la version if let prend en charge le traitement conditionnel (définissant le texte de l'étiquette dans mon cas) et si infoDictionary ou CFBundleShortVersionString sont nuls, le déballage facultatif entraînera le saut du code.

Timothy Tripp
la source
6
self.labelVersion.textest de type facultatif, vous pouvez donc attribuer directementNSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
Jonauz
8
Y a-t-il une raison pour laquelle la valeur ne serait pas définie? D'accord, il est certainement plus prudent let, se demandant simplement pourquoi cela pourrait être nécessaire. Merci!
Crashalot
@Crashalot malgré votre nom;) vous ne voulez pas que votre application plante si, par exemple, vous faites une faute de frappe, plutôt que le numéro de version soit "quelque chose s'est mal passé".
Daniel Springer
OP: vous pouvez remplacer le? avec ! et supprimez "as String". Si c'est nul, ça ne va de toute façon pas tomber en panne
Daniel Springer
245

Mis à jour pour Swift 3.0

Les NSpréfixes ont maintenant disparu dans Swift 3.0 et plusieurs propriétés / méthodes ont changé de nom pour devenir plus Swifty. Voici à quoi cela ressemble maintenant:

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

Bundle.main.releaseVersionNumber
Bundle.main.buildVersionNumber

Ancienne réponse mise à jour

J'ai beaucoup travaillé avec Frameworks depuis ma réponse d'origine, donc je voulais mettre à jour ma solution vers quelque chose qui est à la fois plus simple et beaucoup plus utile dans un environnement multi-bundle:

extension NSBundle {

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

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

}

Maintenant, cette extension sera utile dans les applications pour identifier à la fois le bundle principal et tous les autres bundles inclus (comme un framework partagé pour la programmation d'extensions ou des frameworks tiers comme AFNetworking), comme ceci:

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

// or...

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

Réponse originale

Je voulais améliorer certaines des réponses déjà publiées. J'ai écrit une extension de classe qui peut être ajoutée à votre chaîne d'outils pour gérer cela de manière plus logique.

extension NSBundle {

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

comme? String {return version} return "Numéro de version non disponible"}

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

}

Alors maintenant, vous pouvez y accéder facilement en:

let versionNumber = NSBundle.applicationVersionNumber
Blake Merryman
la source
CFBundleVersionKey ne fonctionne plus dans Swift 3, Xcode 8. Connaissez-vous la nouvelle clé à utiliser?
Crashalot
71

Je sais également que cela a déjà été répondu, mais j'ai résumé les réponses précédentes:

(*) Mis à jour pour les extensions

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

Usage:

someLabel.text = Bundle.main.releaseVersionNumberPretty

@Deprecated: Anciennes réponses

Swift 3.1 :

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

Pour les anciennes versions :

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

Donc, si vous souhaitez définir le texte de l'étiquette ou utiliser ailleurs;

self.labelVersion.text = getVersion()
Gunhan
la source
1
ou: classe func getVersion () -> String {return NSBundle.mainBundle (). infoDictionary? ["CFBundleShortVersionString"] as? Chaîne ?? "aucune information de version"}
tapmonkey
Je pense que copier d'autres réponses n'a pas de sens. Si votre réponse n'est plus valide, vous avez toujours la possibilité de la supprimer et de faire de la place pour les autres réponses :)
carmen_munich
1
@carmen_munich Depuis que vous calomniez ici, je dois vous répondre. Tout d'abord, cette réponse est publiée en mars 2015 et votre réponse est publiée en février 2017. Par conséquent, votre inspiration doit provenir des réponses précédentes. Deuxièmement, je n'ai pas du tout vu votre réponse, j'ai mis à jour ma réponse parce que je l'utilise de cette façon de nos jours. L'utilisation d'une extension n'est pas unique à un membre de la communauté iOS, je suppose. Vraiment, essayez d'être mature et de respecter les autres développeurs. Je ne gagne rien à poster ici. J'aimerais aider les gens c'est tout. Veuillez ne pas décourager les personnes qui tentent d'aider SO.
Gunhan
J'ai entendu beaucoup de commentaires de la part de débutants disant qu'ils publient une réponse et veulent voir que quelqu'un clique sur "up", ce qui est vraiment agréable à voir et motive les gens. Mais si quelqu'un copie des réponses dans sa réponse obsolète, celui qui a fait l'effort de le publier n'obtiendra pas la motivation que quelqu'un l'a votée. Les débutants ressentent donc vraiment de la déception et ont le sentiment de ne pas apporter de valeur à la communauté et de ne plus publier. Et ne vous méprenez pas, je veux dire cela en général. J'espère que vous ne vous sentez pas offensé et que vous comprenez mieux maintenant pourquoi j'ai fait cette suggestion.
carmen_munich
@carmen_munich Si vous ordonnez chronologiquement les réponses à cette question, vous remarquerez que quelqu'un d'autre a déjà donné la même réponse que vous l'avez fait avant vous! Alors tu me blâmes quelque chose que tu as fait toi-même. Depuis que j'ai l'historique de cette question, j'ai partagé mes nouvelles préférences d'utilisation dans une mise à jour. C'est tout.
Gunhan
33

Pour Swift 4.0

let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"]!
let build = Bundle.main.infoDictionary!["CFBundleVersion"]!
Iryna Batvina
la source
29

J'ai fait une extension sur le 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
    }

}

puis l'utiliser

versionLabel.text = "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))"
carmen_munich
la source
En fait, c'est un peu différent. J'utilise le déballage forcé par exemple. Vous pourriez penser que le déballage forcé en général est mauvais, celui-ci est l'un des rares cas où il est correct. Pour l'expliquer un peu plus, ces valeurs devraient être dans le dictionnaire, sinon quelque chose ne va vraiment pas avec le fichier de projet. C'est pourquoi cette approche utilise le
déballage forcé
Renommer et forcer le déballage n'est pas un changement à publier comme nouvelle réponse. De plus, les gens peuvent apprendre que le déballage forcé peut être utilisé n'importe où lorsqu'ils voient votre réponse. Pour les lecteurs, ne présumez pas que la clé est là, il est toujours préférable de gérer manuellement le déballage facultatif plutôt que de forcer le déballage.
Gunhan
Pour être juste, il existe de rares cas où le déballage forcé est acceptable. Ce seul post montre juste un cas où cela peut aller. Si vous voulez en savoir plus sur les cas de déballage forcé, il y a de bonnes explications et tutoriels de Paul Hudson. Je peux vraiment recommander à tous les débutants www.hackingwithswift.com
carmen_munich
C'est une bonne recommandation pour les débutants. Peut-être que vous pouvez également en savoir plus et en savoir plus. Vous devez également améliorer votre compréhension des commentaires et de ce qu'ils impliquent. Par exemple, personne ne vous a dit de ne jamais / jamais utiliser le déballage forcé. Mais pour l'info dict, ces clés peuvent être retirées et le débouclage forcé peut entraîner un crash. Il est plus sûr de gérer le déballage ici.
Gunhan
Peut-être pouvez-vous expliquer dans quel cas ils peuvent être supprimés et être nuls? Ce que j'ai appris de la ressource mentionnée n'est pas dans ce cas particulier. Ils devraient toujours être là à moins que le fichier du projet ne soit cassé, dans ce cas le projet ne serait probablement pas compilé de toute façon
carmen_munich
16

Pour Swift 3.0 NSBundle ne fonctionne pas, le code suivant fonctionne parfaitement.

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

et pour juste le numéro de build, c'est:

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

Confusément 'CFBundleVersion' est le numéro de build tel qu'il est entré dans Xcode sur Général-> Identité.

Tejas
la source
16

Xcode 9.4.1 Swift 4.1

Notez l'utilisation de localizedInfoDictionary pour choisir la bonne version linguistique du nom complet de l'ensemble.

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
    }
}
Hugo F
la source
14

Xcode 8, Swift 3:

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

Swift 4, extension utile pour 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))"
    }
}
maslovsa
la source
Pour l'utiliser, vous devez dire Bundle.main.fullVersion par exemple
Joseph Astrahan
12

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

Usage:

someLabel.text = Bundle.main.versionNumber
Giang
la source
11

OP a demandé le numéro de version et le numéro de build. Malheureusement, la plupart des réponses ne proposent pas ces deux options. De plus, d'autres ajoutent des méthodes d'extension inutiles. En voici un qui est assez simple et résout le problème d'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 ""
  }
}
Sensé
la source
9

Ma réponse (en août 2015), étant donné que Swift continue d'évoluer:

let version = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
Charlie Seligman
la source
8

J'ai créé une extension pour 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"
    }
}
Sebastian Boldt
la source
6

Après avoir examiné la documentation, je pense que ce qui suit est plus propre:

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

Source : "L'utilisation de cette méthode est préférée aux autres méthodes d'accès car elle renvoie la valeur localisée d'une clé lorsqu'elle est disponible."

Daniel Armyr
la source
1
c'est la bonne façon Swift, aucun déballage de force, n'importe où
Juan Boero
5

Pour Swift 1.2, c'est:

let version = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"] as! String
let build = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
Michael Samoylov
la source
1
vous pourriez utiliser? également
Dan Rosenstark
4

Swift 3:

Numéro de version

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

Numéro de build

if let buildNumberString = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { // do something }
jasonnoahchoi
la source
Et le numéro de build? Merci! CFBundleVersionKey ne fonctionne pas.
Crashalot
@Crashalot Je l'ai également mis à jour avec le numéro de build. Voici également une liste de toutes les clés de la fondation de base: developer.apple.com/library/content/documentation/General/…
jasonnoahchoi
3

Swift 4

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

Bundle.main.infoDictionary! ["CFBundleShortVersionString"]

Ancienne syntaxe rapide

let appVer: AnyObject? = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"]
Harshil Kotecha
la source
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
    }

}

Attention: Vous devez utiliser si laissez ici au cas où la version ou la construction de l'application n'est pas définie, ce qui entraînera un plantage si vous essayez d'utiliser! pour déballer.

futantan
la source
2

Voici une version mise à jour pour 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)"
    }
}
BadmintonCat
la source
2

Vous pouvez maintenant utiliser une constante pour cela, plutôt que d'avoir à utiliser du code de type chaîne comme auparavant, ce qui rend les choses encore plus pratiques.

var appVersion: String {
    return Bundle.main.infoDictionary![kCFBundleVersionKey as String] as! String
}
TheNeil
la source
2
public var appVersionNumberString: String {
    get {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
    }
}
Mohammad Razipour
la source
1

SWIFT 4

// Obtenez d'abord le nsObject en le définissant comme AnyObject facultatif

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

// Ensuite, transtypez simplement l'objet en chaîne, mais attention, vous voudrez peut-être vérifier à nouveau zéro

let version = nsObject as! String
Prabhat Kasera
la source
1

pour toute personne intéressée, il existe une bibliothèque agréable et soignée appelée SwifterSwiftdisponible sur github et également entièrement documentée pour chaque version de swift (voir swifterswift.com ).

en utilisant cette bibliothèque, lire la version de l'application et le numéro de build serait aussi simple que cela:

import SwifterSwift

let buildNumber = SwifterSwift.appBuild
let version = SwifterSwift.appVersion
MFA
la source
1

Mise à jour pour Swift 5

voici une fonction que j'utilise pour décider d'afficher ou non une page "l'application mise à jour". Il renvoie le numéro de build, que je convertis en un 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")
    }

S'il n'a jamais été utilisé auparavant, il renverra 0 qui est inférieur au numéro de build actuel. Pour ne pas afficher un tel écran aux nouveaux utilisateurs, ajoutez simplement le numéro de build après la première connexion ou lorsque l'intégration est terminée.

Giovanni Palusa
la source
1

Pour Swift 5.0 :

let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
Délia
la source
1

Fonction utilitaire simple pour renvoyer la version de l'application en tant que 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
    }
Tiago Mendes
la source
1
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
            self.lblAppVersionValue.text = version
        }
Davender Verma
la source
1

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

J'ai combiné les idées de quelques réponses et j'ai étendu un peu:

  • un exemple SwiftUI
  • Affiche une émoticône de triangle d'avertissement (plutôt que de planter l'application) si la clé est manquante dans Info.plist

Fondation d'importation

Bundle d'extension {

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

}


Exemple d'utilisation de SwiftUI

VStack {

     Text("Version: \(Bundle.main.appVersionShort!) (\(Bundle.main.appVersionLong!))")
                    .font(.subheadline)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
}
Gary W. Longsine
la source
0
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
        lblVersion.text = "Version \(version)"

    }
ishwar lal janwa
la source