Come ottengo la versione dell'app e il numero di build utilizzando Swift?


387

Ho un'app IOS con un back-end di Azure e vorrei registrare determinati eventi, come accessi e quali versioni degli utenti dell'app sono in esecuzione.

Come posso restituire la versione e il numero di build utilizzando Swift?



6
Questo è Objective-C, non Swift.
Øyvind Vik,

10
Assicurati di non confondere CFBundleVersione CFBundleShortVersionString`. La prima è la tua versione build . L'altro è il numero di versione . Vedi qui per maggiori informazioni
Miele

1
@ ØyvindVik La maggior parte delle persone presume che tu possa tradurre una soluzione Objective-C in Swift senza tenere la mano.
gnasher729,

Risposte:


425

MODIFICARE

Aggiornato per Swift 4.2

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

MODIFICARE

Come sottolineato da @azdev sulla nuova versione di Xcode otterrai un errore di compilazione per provare la mia soluzione precedente, per risolverlo basta modificarlo come suggerito per scartare il dizionario del bundle usando un!

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

Fine modifica

Usa solo la stessa logica rispetto a Objective-C ma con alcune piccole modifiche

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

Spero che questo ti aiuti.

David


Quando lo uso, visualizzo un errore del compilatore "[NSObject: AnyObject]? Non ha un membro chiamato 'subscript'"
andreas

Puoi provare a sostituire AnyObject? di AnyObject! senza conoscere esattamente il codice che stai utilizzando è molto difficile indovinare cosa non va, tieni anche presente che Swift può passare da una versione Xcode a un'altra, quindi sarà anche importante conoscere la tua versione di Xcode.
David

18
@andreas infoDictionarydovrebbe essere scartato usando! . Questo è quello che sto usando, inserito in un file Globals.swift:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
azdev,

1
Ho dovuto aggiungere un altro "!" dopo "as".let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
yosei,

3
Dovresti evitare di usare un involucro forzato "!" poiché causeranno l'arresto anomalo della tua app ogni volta che uno di questi valori è zero
Julius

279

So che è già stata data una risposta, ma personalmente penso che sia un po 'più pulito:

Swift 3.0:

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

Swift <2.3

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

In questo modo, la versione if let si occupa dell'elaborazione condizionale (impostando il testo dell'etichetta nel mio caso) e se infoDictionary o CFBundleShortVersionString sono nulli, lo scartamento opzionale provocherà il salto del codice.


6
self.labelVersion.textè di tipo opzionale, quindi puoi assegnare direttamenteNSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
Jonauz il

8
C'è una ragione per cui il valore non sarebbe impostato? D'accordo, è decisamente più cauto let, chiedendosi solo perché potrebbe essere necessario. Grazie!
Crashalot,

@Crashalot nonostante il tuo nome;) non vuoi che la tua app vada in crash se, diciamo, fai un refuso, piuttosto che il numero di versione sia "qualcosa è andato storto".
Daniel Springer,

OP: è possibile sostituire il? con ! e rimuovi "come stringa". Se è zero, non si schianterà comunque
Daniel Springer

245

Aggiornato per Swift 3.0

I NSprefissi sono ora spariti in Swift 3.0 e diverse proprietà / metodi hanno cambiato i nomi per essere più Swifty. Ecco come appare ora:

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

Bundle.main.releaseVersionNumber
Bundle.main.buildVersionNumber

Vecchia risposta aggiornata

Ho lavorato molto con Frameworks dalla mia risposta originale, quindi volevo aggiornare la mia soluzione a qualcosa di più semplice e molto più utile in un ambiente multi-bundle:

extension NSBundle {

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

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

}

Ora questa estensione sarà utile nelle app per identificare sia il bundle principale sia qualsiasi altro bundle incluso (come un framework condiviso per la programmazione di estensioni o terzi framework come AFNetworking), in questo modo:

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

// or...

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

Risposta originale

Volevo migliorare alcune delle risposte già pubblicate. Ho scritto un'estensione di classe che può essere aggiunta alla catena degli strumenti per gestirla in modo più logico.

extension NSBundle {

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

come? String {return version} return "Numero versione non disponibile"}

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

}

Quindi ora puoi accedervi facilmente:

let versionNumber = NSBundle.applicationVersionNumber

CFBundleVersionKey non funziona più in Swift 3, Xcode 8. Conosci la nuova chiave da usare?
Crashalot,

71

So anche che è già stata data una risposta, ma ho racchiuso le risposte precedenti:

(*) Aggiornato per le estensioni

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

Uso:

someLabel.text = Bundle.main.releaseVersionNumberPretty

@ Decrementato: vecchie risposte

Swift 3.1 :

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

Per le versioni precedenti :

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

Quindi, se si desidera impostare il testo dell'etichetta o si desidera utilizzare altrove;

self.labelVersion.text = getVersion()

1
oppure: class func getVersion () -> String {return NSBundle.mainBundle (). infoDictionary? ["CFBundleShortVersionString"] as? Corda ?? "nessuna informazione sulla versione"}
tapmonkey

Penso che copiare altre risposte non abbia senso. Se la tua risposta non è più valida, hai sempre la possibilità di eliminarla e fare spazio alle altre risposte :)
carmen_munich,

1
@carmen_munich Da quando hai calunniato qui, devo risponderti. Prima di tutto, questa risposta è pubblicata a marzo 2015 e la tua risposta è pubblicata a febbraio 2017. Pertanto, l'ispirazione deve provenire dalle risposte precedenti. In secondo luogo, non ho visto affatto la tua risposta, ho aggiornato la mia risposta perché la utilizzo così al giorno d'oggi. L'uso di un'estensione non è univoco per qualcuno nella comunità iOS, suppongo. Davvero, per favore, cerca di essere maturo e rispetta gli altri sviluppatori. Non ottengo nulla postando qui. Vorrei aiutare le persone. Si prega di cercare di non scoraggiare le persone che cercano di aiutare su SO.
Gunhan,

Ho sentito un sacco di feedback dai neofiti che postano una risposta e vogliono vedere che qualcuno fa clic su "su", che è davvero bello da vedere e motiva le persone. Ma se qualcuno copia le risposte nella sua risposta obsoleta, chi ha fatto lo sforzo di pubblicarlo non otterrà quella motivazione che qualcuno ha votato. Quindi i neofiti provano davvero delusione e hanno la sensazione di non apportare valore alla comunità e di interrompere la pubblicazione. E non sbagli, intendo questo in generale. Spero che tu non ti senta offeso e capisca meglio perché ho dato questo suggerimento.
carmen_munich,

@carmen_munich Se ordini cronologicamente le risposte a questa domanda, noterai che qualcun altro ha già dato la stessa risposta che hai fatto prima di te! Quindi mi stai incolpando per qualcosa che ti sei fatto da solo. Poiché ho una cronologia di questa domanda, ho condiviso le mie nuove preferenze di utilizzo in un aggiornamento. È tutto.
Gunhan,

32

Per Swift 4.0

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

29

Ho fatto un'estensione sul pacchetto

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
    }

}

e poi usalo

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

In realtà è un po 'diverso. Per esempio, uso lo scollamento forzato. Potresti pensare che scartare la forza in generale sia negativo, questo è uno dei rari casi in cui va bene. Per spiegarlo un po 'di più, questi valori dovrebbero essere nel dizionario, altrimenti c'è qualcosa di veramente sbagliato nel file di progetto. Questo è il motivo per cui questo approccio usa la forza di
scartare

Rinominare e forzare il disimballaggio non è una modifica da pubblicare come nuova risposta. Inoltre, le persone potrebbero imparare che lo scartamento forzato potrebbe essere usato ovunque quando vedono la tua risposta. Per i lettori, non dare per scontato che la chiave sia lì, è sempre meglio gestire lo srotolamento manuale opzionale piuttosto che forzare lo srotolamento.
Gunhan,

Ad essere onesti ci sono alcuni rari casi in cui lo scartamento forzato va bene. Questo post mostra solo un caso in cui può andare bene. Se vuoi saperne di più sui casi di scartamento forzato, ci sono alcune buone spiegazioni e tutorial di Paul Hudson. Posso davvero consigliare a tutti i neofiti www.hackingwithswift.com
carmen_munich

È una buona raccomandazione per i neofiti. Forse puoi anche leggere di più e saperne di più. Inoltre, dovresti migliorare la tua comprensione dei commenti e di ciò che implicano. Ad esempio, nessuno ti ha mai detto di non usare mai / mai la forza da scartare. Ma per le informazioni dettate quelle chiavi possono essere rimosse e la forza di scartare può provocare un crash. È più sicuro gestire lo scartamento qui.
Gunhan,

Forse puoi spiegare in che caso possono essere rimossi ed essere nulli? Ciò che ho imparato dalla risorsa menzionata non è in questo caso particolare. Dovrebbero essere sempre lì a meno che il file del progetto non sia rotto, in quel caso probabilmente il progetto non si comporterebbe comunque
carmen_munich

16

Per Swift 3.0 NSBundle non funziona, il codice seguente funziona perfettamente.

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

e solo per il numero di build, è:

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

Confusivamente 'CFBundleVersion' è il numero di build immesso in Xcode in Generale-> Identità.


16

Xcode 9.4.1 Swift 4.1

Nota l'uso di localizedInfoDictionary per selezionare la versione della lingua corretta del nome visualizzato del bundle.

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"

13

Swift 4, utile estensione per 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))"
    }
}

Per usarlo devi dire Bundle.main.fullVersion per esempio
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
    }
}

Uso:

someLabel.text = Bundle.main.versionNumber

11

OP ha chiesto sia il numero di versione che il numero di build. Sfortunatamente la maggior parte delle risposte non fornisce entrambe queste opzioni. Inoltre, altri aggiungono metodi di estensione non necessari. Eccone uno piuttosto semplice e risolve il problema di 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

La mia risposta (ad agosto 2015), dato che Swift continua a evolversi:

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

8

Ho creato un'estensione per 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

Dopo aver esaminato la documentazione, credo che quanto segue sia più pulito:

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

Fonte : "L'uso di questo metodo è preferito rispetto ad altri metodi di accesso perché restituisce il valore localizzato di una chiave quando è disponibile."


1
questo è il giusto modo rapido, nessuna forza da scartare, ovunque
Juan Boero,

5

Per Swift 1.2 è:

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

1
potresti usare? anche
Dan Rosenstark il

4

Swift 3:

Numero della versione

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

Numero di build

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

Che ne dici del numero di build? Grazie! CFBundleVersionKey non funziona.
Crashalot,

@Crashalot L'ho aggiornato anche con il numero di build. Ecco anche un elenco di tutte le chiavi della Core Foundation: developer.apple.com/library/content/documentation/General/…
jasonnoahchoi

3

Swift 4

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

Bundle.main.infoDictionary! [ "CFBundleShortVersionString"]

Vecchia sintassi rapida

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
    }

}

Attenzione: dovresti usarlo se lasciato qui nel caso in cui la versione o la build dell'app non sia impostata, il che provocherà un arresto anomalo se provi a utilizzare! da scartare.


2

Ecco una versione aggiornata per 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

Ora puoi usare una costante per questo, piuttosto che dover usare un codice tipizzato come prima, il che rende le cose ancora più convenienti.

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

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

1

SWIFT 4

// Per prima cosa ottieni nsObject definendolo come AnyObject opzionale

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

// Quindi esegui il cast dell'oggetto come stringa, ma fai attenzione, potresti voler ricontrollare per zero

let version = nsObject as! String

1

per chiunque sia interessato, c'è una libreria bella e ordinata chiamata SwifterSwiftdisponibile su github e completamente documentata per ogni versione di swift (vedi swifterswift.com ).

usando questa libreria, leggere la versione dell'app e il numero di build sarebbe facile come questo:

import SwifterSwift

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

1

Aggiornamento per Swift 5

ecco una funzione che sto usando per decidere se mostrare o meno una pagina "l'app aggiornata". Restituisce il numero di build, che sto convertendo in 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")
    }

Se mai usato prima, restituirà 0 che è inferiore al numero di build corrente. Per non mostrare una schermata del genere ai nuovi utenti, è sufficiente aggiungere il numero di build dopo il primo accesso o al termine dell'onboarding.


1

Per Swift 5.0 :

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

1

Semplice funzione di utilità per restituire la versione dell'app come 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
    }

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

1

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

Ho combinato idee da alcune risposte e ho esteso un po ':

  • un esempio di SwiftUI
  • Visualizza un'emoticon del triangolo di avvertimento (anziché arrestare in modo anomalo l'app) se la chiave non è presente in Info.plist

Fondazione di importazione

pacchetto estensione {

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

}


Esempio di utilizzo di SwiftUI

VStack {

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

0
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
        lblVersion.text = "Version \(version)"

    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.