Como obtenho a versão do aplicativo e construo o número usando o Swift?

387

Eu tenho um aplicativo IOS com um back-end do Azure e gostaria de registrar certos eventos, como logons e quais versões dos usuários do aplicativo estão em execução.

Como posso devolver a versão e criar o número usando o Swift?

Øyvind Vik
fonte
6
Isso é Objective-C, não Swift.
Øyvind Vik 22/09
10
Certifique-se de não confundir CFBundleVersion& CFBundleShortVersionString`. A primeira é a sua versão de compilação . O outro é o número da versão . Veja aqui para mais informações
Honey
11
@ ØyvindVik A maioria das pessoas supõe que você pode traduzir uma solução Objective-C para Swift sem segurar a mão.
gnasher729

Respostas:

424

EDITAR

Atualizado para o Swift 4.2

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

EDITAR

Como apontado por @azdev na nova versão do Xcode, você receberá um erro de compilação por tentar minha solução anterior. Para resolver isso, edite-o conforme sugerido para desembrulhar o dicionário de pacotes usando a!

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

Finalizar edição

Basta usar a mesma lógica que no Objective-C, mas com algumas pequenas alterações

//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 isso ajude você.

David

David
fonte
Quando eu uso isso, recebo um erro do compilador "[NSObject: AnyObject]? Não tem um membro chamado 'subscrito'"
andreas
Você pode tentar substituir o AnyObject? por AnyObject! sem saber exatamente o código que você está usando, é muito difícil adivinhar o que está errado, mas lembre-se de que o Swift pode mudar de uma versão do Xcode para outra, portanto, também será relevante conhecer sua versão do Xcode.
David
18
@andreas infoDictionarydeve ser desembrulhado usando !. Isto é o que eu estou usando, colocado em um arquivo Globals.swift:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
azdev 10/11/14
11
Eu tive que adicionar mais um "!" depois de "como". let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
yosei
3
Você deve evitar usar um desembrulhamento forçado "!" como eles vão fazer com que seu aplicativo para acidente sempre que um desses valores é nulo
Julius
279

Eu sei que isso já foi respondido, mas pessoalmente acho que isso é um pouco mais limpo:

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
}

Dessa forma, a versão if let cuida do processamento condicional (configurando o texto do rótulo no meu caso) e se infoDictionary ou CFBundleShortVersionString forem nulos, o desempacotamento opcional fará com que o código seja ignorado.

Timothy Tripp
fonte
6
self.labelVersion.texté do tipo opcional, para que você possa atribuir diretamenteNSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
Jonauz 25/11
8
Existe uma razão para o valor não ser definido? Concordou que é definitivamente mais cauteloso let, apenas se perguntando por que pode ser necessário. Obrigado!
Crashalot 8/16
@Crashalot, apesar do seu nome;) você não quer que seu aplicativo falhe se, digamos, digitar um erro, preferir que o número da versão seja "algo deu errado".
9788 Daniel Springer
OP: você pode substituir o? com! e remova "como String". Se for nulo, é de qualquer maneira não vai falhar
Daniel Springer
245

Atualizado para o Swift 3.0

Os NSprefixos-agora desapareceram no Swift 3.0 e várias propriedades / métodos mudaram de nome para serem mais Swifty. Aqui está o que isso parece agora:

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

Bundle.main.releaseVersionNumber
Bundle.main.buildVersionNumber

Resposta atualizada antiga

Eu tenho trabalhado muito com Frameworks desde a minha resposta original, então eu queria atualizar minha solução para algo que seja mais simples e muito mais útil em um ambiente com vários pacotes:

extension NSBundle {

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

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

}

Agora, esta extensão será útil em aplicativos para identificar o pacote principal e quaisquer outros pacotes incluídos (como uma estrutura compartilhada para programação de extensões ou terceira estrutura como AFNetworking), da seguinte forma:

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

// or...

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

Resposta original

Eu queria melhorar algumas das respostas já postadas. Eu escrevi uma extensão de classe que pode ser adicionada à sua cadeia de ferramentas para lidar com isso de uma maneira mais lógica.

extension NSBundle {

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

Como? String {return version} return "Número da versão não disponível"}

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

}

Então agora você pode acessar isso facilmente por:

let versionNumber = NSBundle.applicationVersionNumber
Blake Merryman
fonte
CFBundleVersionKey não funciona mais no Swift 3, Xcode 8. Você conhece a nova chave a ser usada?
Crashalot
71

Sei também que isso já foi respondido, mas encerrei as respostas anteriores:

(*) Atualizado para extensões

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: respostas antigas

Swift 3.1 :

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

Para versões mais antigas :

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

Portanto, se você deseja definir o texto do rótulo ou usar em outro lugar;

self.labelVersion.text = getVersion()
Gunhan
fonte
11
ou: classe func getVersion () -> String {retornar NSBundle.mainBundle (). infoDictionary? ["CFBundleShortVersionString"] como? Corda ?? "no version info"}
tapmonkey 17/06/2015
Eu acho que copiar outras respostas não faz sentido. Se a sua resposta não é mais válido, então você sempre tem a possibilidade de excluí-lo e dar espaço para as outras respostas :)
carmen_munich
11
@carmen_munich Desde que você calunia aqui, tenho que responder a você. Antes de tudo, esta resposta foi publicada em março de 2015 e sua resposta foi publicada em fevereiro de 2017. Portanto, sua inspiração deve vir das respostas anteriores. Em segundo lugar, eu não vi sua resposta, atualizei minha resposta porque a uso dessa maneira hoje em dia. Usar uma extensão não é exclusivo de alguém da comunidade iOS, eu acho. Realmente, tente ser maduro e respeitar outros desenvolvedores. Não ganho nada postando aqui. Eu gostaria de ajudar as pessoas. Por favor, tente não desencorajar as pessoas que tentam ajudar no SO.
Gunhan
Eu ouvi muitos comentários de novatos de que eles postam uma resposta e querem ver que alguém clica em "para cima", o que é muito bom de ver e motiva as pessoas. Mas se alguém copiar respostas em sua resposta desatualizada, quem fez o esforço para publicá-la não obterá a motivação que alguém votou. Então, os novatos ficam realmente desapontados e sentem que não agregam valor à comunidade e param de postar. E não entenda errado, quero dizer isso em geral. Espero que você não se sinta ofendido e entenda agora melhor por que fiz essa sugestão.
Carmen_munich 23/04/19
@carmen_munich Se você pedir cronologicamente as respostas para esta pergunta, perceberá que alguém já deu a mesma resposta que você fez antes de você! Então você está me culpando por algo que você mesma fez. Como tenho histórico para esta pergunta, compartilhei minhas novas preferências de uso em uma atualização. Isso é tudo.
Gunhan
32

Para Swift 4.0

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

Eu fiz uma extensão no pacote

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 depois use

versionLabel.text = "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))"
carmen_munich
fonte
Na verdade, é um pouco diferente. Eu uso a força de desembrulhar, por exemplo. Você pode pensar que a força de desembrulhar em geral é ruim, este é um dos raros casos em que é aceitável. Para explicar um pouco mais, esses valores devem estar no dicionário, caso contrário, algo está realmente errado com o arquivo do projeto. É por isso que esta abordagem é usando a força desembrulhar :-)
carmen_munich
Renomear e forçar o desempacotamento não é uma alteração a ser postada como uma nova resposta. Além disso, as pessoas podem aprender que o desembrulhamento forçado pode ser usado em qualquer lugar quando virem sua resposta. Para os leitores, não assuma que a chave está lá, é sempre melhor lidar com o desembrulhamento opcional manualmente, em vez de forçar o desembrulhamento.
Gunhan
Para ser justo, existem alguns casos raros em que o desembrulhamento forçado é bom. Este post mostra apenas um caso em que tudo pode ficar bem. Se você quiser saber mais sobre os casos de desembrulhamento forçado, existem algumas boas explicações e tutoriais de Paul Hudson. Eu realmente recomendo a todos novatos www.hackingwithswift.com
carmen_munich
É uma boa recomendação para os novatos. Talvez você também possa ler mais e aprender mais. Além disso, você deve melhorar sua compreensão sobre os comentários e o que eles implicam. Por exemplo, ninguém disse para você nunca / nunca usar força para desembrulhar. Mas, para o ditado informativo, essas chaves podem ser removidas e a força de desembrulhar pode resultar em um acidente. É mais seguro lidar com desembrulhar aqui.
Gunhan
Talvez você possa explicar em que caso eles podem ser removidos e nulos? O que aprendi com o recurso mencionado não está neste caso de partiuclare. Eles devem estar sempre lá, a menos que o arquivo de projeto está quebrado, nesse caso, o projeto provavelmente não compilar de qualquer maneira
carmen_munich
16

Para o Swift 3.0, o NSBundle não funciona, o código a seguir funciona perfeitamente.

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

e apenas para o número da compilação, é:

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

Confusamente 'CFBundleVersion' é o número da compilação, conforme digitado no Xcode em Geral-> Identidade.

Tejas
fonte
16

Xcode 9.4.1 Swift 4.1

Observe o uso do localizedInfoDictionary para selecionar a versão do idioma correto do nome de exibição do pacote configurável.

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

Xcode 8, Swift 3:

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

Swift 4, Extensão ú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
fonte
Para usar isso, você precisa dizer Bundle.main.fullVersion, por exemplo
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
Giang
fonte
11

O OP solicitou o número da versão e o número da compilação. Infelizmente, a maioria das respostas não fornece essas duas opções. Além disso, outros adicionam métodos de extensão desnecessários. Aqui está um que é bem simples e resolve o problema do 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ível
fonte
9

Minha resposta (em agosto de 2015), dado o Swift continuar evoluindo:

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

Eu criei uma extensão 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
fonte
6

Tendo examinado a documentação, acredito que o seguinte é mais limpo:

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

Fonte : "O uso desse método é preferível a outros métodos de acesso, pois retorna o valor localizado de uma chave quando uma está disponível."

Daniel Armyr
fonte
11
essa é a maneira rápida, sem forçar a desembrulhar, em qualquer lugar #
Juan Boero
5

Para o Swift 1.2, é:

let version = NSBundle.mainBundle().infoDictionary!["CFBundleShortVersionString"] as! String
let build = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
Michael Samoylov
fonte
11
você poderia usar? bem
Dan Rosenstark
4

Swift 3:

Número da versão

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

Número da compilação

if let buildNumberString = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { // do something }
jasonnoahchoi
fonte
E o número da compilação? Obrigado! CFBundleVersionKey não funciona.
Crashalot 25/09/16
@Crashalot Atualizei-o com o número da compilação também. Aqui está também uma lista de todos Núcleo Fundação Chaves: developer.apple.com/library/content/documentation/General/...
jasonnoahchoi
3

Swift 4

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

Bundle.main.infoDictionary! ["CFBundleShortVersionString"]

Sintaxe antiga rápida

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

}

Atenção: Você deve usar if let here, caso a versão ou compilação do aplicativo não esteja definida, o que levará ao travamento se você tentar usar! desembrulhar.

futantan
fonte
2

Aqui está uma versão atualizada do 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
fonte
2

Agora você pode usar uma constante para isso, em vez de precisar usar um código digitado em seqüência como antes, o que torna as coisas ainda mais convenientes.

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

SWIFT 4

// Primeiro obtenha o nsObject definindo como um AnyObject opcional

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

// Em seguida, basta converter o objeto como uma String, mas tenha cuidado, verifique duas vezes se não há

let version = nsObject as! String
Prabhat Kasera
fonte
1

para qualquer pessoa interessada, há uma biblioteca agradável e organizada chamada SwifterSwiftdisponível no github e também totalmente documentada para todas as versões do swift (consulte swifterswift.com ).

usando esta biblioteca, ler a versão do aplicativo e o número da compilação seria tão fácil quanto isto:

import SwifterSwift

let buildNumber = SwifterSwift.appBuild
let version = SwifterSwift.appVersion
MFA
fonte
1

Atualização para o Swift 5

aqui está uma função que estou usando para decidir se mostra ou não uma página "o aplicativo atualizado". Ele retorna o número da compilação, que eu estou convertendo para um 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 nunca usado antes, retornará 0, que é menor que o número da compilação atual. Para não mostrar essa tela para novos usuários, basta adicionar o número da compilação após o primeiro login ou quando a integração for concluída.

Giovanni Palusa
fonte
1

Para o Swift 5.0 :

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

Função utilitário simples para retornar a versão do aplicativo 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
fonte
1
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
            self.lblAppVersionValue.text = version
        }
Davender Verma
fonte
1

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

Combinei idéias com algumas respostas e estendi um pouco:

  • um exemplo SwiftUI
  • Exibe um emoticon de triângulo de aviso (em vez de travar o aplicativo) se a chave estiver ausente no Info.plist

Fundação de importação

pacote de extensão {

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

}


Exemplo de uso do SwiftUI

VStack {

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

    }
ishwar lal janwa
fonte