¿Cómo obtengo la versión de la aplicación y el número de compilación usando Swift?

387

Tengo una aplicación IOS con un back-end de Azure, y me gustaría registrar ciertos eventos, como inicios de sesión y qué versiones de los usuarios de la aplicación se están ejecutando.

¿Cómo puedo devolver la versión y el número de compilación usando Swift?

Øyvind Vik
fuente
66
Eso es Objective-C, no Swift.
Øyvind Vik
10
Asegúrese de no confundir CFBundleVersion& CFBundleShortVersionString`. El primero es su versión de compilación . El otro es el número de versión . Ver aquí para más información
Miel
1
@ ØyvindVik La mayoría de las personas asumen que puede traducir una solución de Objective-C a Swift sin la mano.
gnasher729

Respuestas:

425

EDITAR

Actualizado para Swift 4.2

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

EDITAR

Como señaló @azdev en la nueva versión de Xcode, obtendrá un error de compilación al probar mi solución anterior, para resolver esto solo edítelo como se sugiere para desenvolver el diccionario de paquetes usando a!

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

Fin de edición

Simplemente use la misma lógica que en Objective-C pero con algunos pequeños cambios

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

Espero que esto te ayude.

David

David
fuente
Cuando uso esto, aparece un error de compilación "[NSObject: AnyObject]? No tiene un miembro llamado 'subíndice'"
andreas
¿Puedes intentar reemplazar AnyObject? por AnyObject! sin saber exactamente el código que está utilizando es muy difícil adivinar qué está mal, también tenga en cuenta que Swift puede cambiar de una versión de Xcode a otra, por lo que también será relevante conocer su versión de Xcode.
David
18
@andreas se infoDictionarydebe desenvolver usando !. Esto es lo que estoy usando, colocado en un archivo Globals.swift:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
azdev
1
Tuve que agregar uno más "!" después de "como". let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
yosei
3
Debe evitar usar un desenvolvimiento forzado "!" ya que harán que su aplicación se bloquee cuando uno de esos valores sea nulo
Julio
279

Sé que esto ya ha sido respondido, pero personalmente creo que esto es un poco más limpio:

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
}

De esta manera, la versión if let se encarga del procesamiento condicional (configurando el texto de la etiqueta en mi caso) y si infoDictionary o CFBundleShortVersionString son nulos, el desenvolvimiento opcional hará que se omita el código.

Timothy Tripp
fuente
66
self.labelVersion.textes tipo opcional, por lo que puede asignar directamenteNSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
Jonauz
8
¿Hay alguna razón por la cual el valor no se establecería? De acuerdo es definitivamente más cauteloso let, solo preguntándome por qué podría ser necesario. ¡Gracias!
Crashalot
@Crashalot a pesar de su nombre;) no desea que su aplicación se bloquee si, por ejemplo, comete un error tipográfico, en lugar de que el número de versión sea "algo salió mal".
Daniel Springer
OP: puede reemplazar el? con ! y elimine "como cadena". Si es nulo, de todos modos no se estrellará
Daniel Springer
245

Actualizado para Swift 3.0

Los NSprefijos ya no están en Swift 3.0 y varias propiedades / métodos han cambiado de nombre para ser más Swifty. Así es como se ve ahora:

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

Bundle.main.releaseVersionNumber
Bundle.main.buildVersionNumber

Antigua respuesta actualizada

He trabajado mucho con Frameworks desde mi respuesta original, por lo que quería actualizar mi solución a algo que sea más simple y mucho más útil en un entorno de paquetes múltiples:

extension NSBundle {

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

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

}

Ahora esta extensión será útil en aplicaciones para identificar tanto el paquete principal como cualquier otro paquete incluido (como un marco compartido para la programación de extensiones o terceros marcos como AFNetworking), de esta manera:

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

// or...

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

Respuesta original

Quería mejorar algunas de las respuestas ya publicadas. Escribí una extensión de clase que se puede agregar a su cadena de herramientas para manejar esto de una manera más lógica.

extension NSBundle {

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

¿como? Cadena {return version} return "Número de versión no disponible"}

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

}

Así que ahora puedes acceder a esto fácilmente al:

let versionNumber = NSBundle.applicationVersionNumber
Blake Merryman
fuente
CFBundleVersionKey ya no funciona en Swift 3, Xcode 8. ¿Conoces la nueva clave para usar?
Crashalot
71

También sé que esto ya ha sido respondido, pero resumí las respuestas anteriores:

(*) Actualizado para extensiones

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

@Deprecated: respuestas anteriores

Swift 3.1 :

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

Para versiones anteriores :

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

Entonces, si desea establecer el texto de la etiqueta o desea usarlo en otro lugar;

self.labelVersion.text = getVersion()
Gunhan
fuente
1
o: class func getVersion () -> String {return NSBundle.mainBundle (). infoDictionary? ["CFBundleShortVersionString"] como? Cuerda ?? "sin información de versión"}
tapmonkey
Creo que copiar otras respuestas no tiene sentido. Si su respuesta ya no es válida, siempre tiene la posibilidad de eliminarla y dejar espacio para las otras respuestas :)
carmen_munich
1
@carmen_munich Como calumnias aquí, tengo que responderte. En primer lugar, esta respuesta se publica en marzo de 2015 y su respuesta se publica en febrero de 2017. Por lo tanto, su inspiración debe provenir de las respuestas anteriores. En segundo lugar, no he visto su respuesta en absoluto, actualicé mi respuesta porque la uso de esta manera hoy en día. Supongo que usar una extensión no es exclusivo de alguien de la comunidad iOS. Realmente, intenta ser maduro y respetar a otros desarrolladores. No gano nada publicando aquí. Me gustaría ayudar a la gente, eso es todo. Intenta no desanimar a las personas que intentan ayudar en SO.
Gunhan
Escuché una gran cantidad de comentarios de los novatos que publican una respuesta y quieren ver que alguien hace clic en "arriba", lo que es realmente agradable de ver y motiva a las personas. Pero si alguien copia las respuestas en su respuesta obsoleta, el que hizo el esfuerzo de publicarla no obtendrá la motivación de que alguien la haya votado. Entonces, los novatos se sienten realmente decepcionados y tienen la sensación de que no aportan valor a la comunidad y dejan de publicar. Y no te equivoques, quiero decir esto en general. Espero que no te sientas ofendido y entiendas ahora mejor por qué hice esta sugerencia.
carmen_munich
@carmen_munich ¡Si ordena cronológicamente las respuestas a esta pregunta, notará que otra persona ya dio la misma respuesta que usted antes! Así que me estás culpando de algo que tú mismo habías hecho. Como tengo historial para esta pregunta, compartí mis nuevas preferencias de uso en una actualización. Eso es todo.
Gunhan
32

Para Swift 4.0

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

Hice una extensión en el paquete

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
    }

}

y luego úsalo

versionLabel.text = "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))"
carmen_munich
fuente
En realidad es un poco diferente. Utilizo el desenvolvimiento forzado, por ejemplo. Puede pensar que el desenvolvimiento forzado en general es malo, este es uno de los raros casos en los que está bien. Para explicarlo un poco más, estos valores deberían estar en el diccionario; de lo contrario, algo está realmente mal con el archivo del proyecto. Es por eso que este enfoque utiliza el desenvolvimiento forzado :-)
carmen_munich
Cambiar el nombre y desenvolver la fuerza no es un cambio para publicar como una nueva respuesta. Además, las personas podrían aprender que el desenvolvimiento forzado podría usarse en cualquier lugar cuando vean su respuesta. Para los lectores, no asuman que la clave está ahí, siempre es mejor manejar manualmente el desenvolvimiento opcional en lugar de forzar el desenvolvimiento.
Gunhan
Para ser justos, hay algunos casos raros en los que el desenvolvimiento forzado está bien. Esta publicación solo muestra un caso en el que puede estar bien. Si desea saber más sobre los casos de desenvolvimiento forzado, hay algunas buenas explicaciones y tutoriales de Paul Hudson. Realmente puedo recomendar a todos los novatos www.hackingwithswift.com
carmen_munich
Es una buena recomendación para los novatos. Quizás también puedas leer más y aprender más. También debe mejorar su comprensión sobre los comentarios y lo que implican. Por ejemplo, nadie te dijo que nunca / nunca uses el desenvolvimiento forzado. Pero para la información dict esas claves se pueden eliminar y la fuerza de desenvolver puede provocar un bloqueo. Es más seguro manejar el desenvolvimiento aquí.
Gunhan
¿Quizás puedas explicar en qué caso pueden ser eliminados y ser nulos? Lo que aprendí del recurso mencionado no es en este caso particular. Deben estar siempre allí a menos que el archivo de proyecto se divide, en ese caso el proyecto probablemente no compilar todos modos
carmen_munich
16

Para Swift 3.0 NSBundle no funciona, el siguiente código funciona perfectamente.

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

y solo por el número de compilación, es:

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

Confusamente, 'CFBundleVersion' es el número de compilación que se ingresó en Xcode en General-> Identidad.

Tejas
fuente
16

Xcode 9.4.1 Swift 4.1

Tenga en cuenta el uso de localizedInfoDictionary para elegir la versión de idioma correcta del nombre para mostrar del paquete.

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
fuente
14

Xcode 8, Swift 3:

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

Swift 4, extensión útil para 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
fuente
Para usar esto, debe decir Bundle.main.fullVersion, por ejemplo
Joseph Astrahan
12

Paquete + Extensiones.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
Giang
fuente
11

OP solicitó tanto el número de versión como el número de compilación. Desafortunadamente, la mayoría de las respuestas no brindan ambas opciones. Además, otros agregan métodos de extensión innecesarios. Aquí hay uno que es bastante simple y resuelve el problema de 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 ""
  }
}
Sensato
fuente
9

Mi respuesta (a agosto de 2015), dado que Swift sigue evolucionando:

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

Creé una extensión para 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
fuente
6

Después de mirar la documentación, creo que lo siguiente es más limpio:

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

Fuente : "El uso de este método es preferible a otros métodos de acceso porque devuelve el valor localizado de una clave cuando hay una disponible".

Daniel Armyr
fuente
1
esa es la manera correcta de Swift, sin forzar el desenvolvimiento, en cualquier lugar
Juan Boero
5

Para Swift 1.2 es:

let version = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"] as! String
let build = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
Michael Samoylov
fuente
1
podrías usar? también
Dan Rosenstark
4

Swift 3:

Número de versión

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

Número de compilación

if let buildNumberString = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { // do something }
jasonnoahchoi
fuente
¿Qué tal el número de compilación? ¡Gracias! CFBundleVersionKey no funciona.
Crashalot
@Crashalot También lo actualicé con el número de compilación. Aquí también hay una lista de todas las claves de 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"]

Rápida sintaxis antigua

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

}

Atención: debe usarlo si lo deja aquí en caso de que la versión o compilación de la aplicación no esté configurada, lo que provocará un bloqueo si intenta usarlo. para desenvolver.

futantan
fuente
2

Aquí hay una versión actualizada para 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)"
    }
}
BádmintonGato
fuente
2

Ahora puede usar una constante para esto, en lugar de tener que usar código de tipo secuencial como antes, lo que hace que las cosas sean aún más convenientes.

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

SWIFT 4

// Primero obtenga el nsObject definiéndolo como un AnyObject opcional

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

// Luego simplemente lanza el objeto como una Cadena, pero ten cuidado, es posible que quieras verificar dos veces si es nulo

let version = nsObject as! String
Prabhat Kasera
fuente
1

Para cualquier persona interesada, hay una biblioteca agradable y ordenada SwifterSwiftdisponible en github y también totalmente documentada para cada versión de swift (ver swifterswift.com ).

usando esta biblioteca, leer la versión de la aplicación y el número de compilación sería tan fácil como esto:

import SwifterSwift

let buildNumber = SwifterSwift.appBuild
let version = SwifterSwift.appVersion
AMF
fuente
1

Actualización para Swift 5

Aquí hay una función que estoy usando para decidir si mostrar una página "la aplicación actualizada" o no. Devuelve el número de compilación, que estoy convirtiendo 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")
    }

Si nunca se usó antes, devolverá 0, que es inferior al número de compilación actual. Para no mostrar dicha pantalla a los nuevos usuarios, simplemente agregue el número de compilación después del primer inicio de sesión o cuando se complete la incorporación.

Giovanni Palusa
fuente
1

Para Swift 5.0 :

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

Función de utilidad simple para devolver la versión de la aplicación como 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
fuente
1
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
            self.lblAppVersionValue.text = version
        }
Davender Verma
fuente
1

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

Combiné ideas de algunas respuestas y extendí un poco:

  • un ejemplo de SwiftUI
  • Muestra un emoticón de triángulo de advertencia (en lugar de bloquear la aplicación) si falta la clave en la lista de información.

Fundación de importación

paquete de extensión {

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

}


Uso de ejemplo de SwiftUI

VStack {

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

    }
ishwar lal janwa
fuente