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?
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
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:
iflet version =Bundle.main.infoDictionary?["CFBundleShortVersionString"]as?String{self.labelVersion.text = version
}
Swift <2.3
iflet 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.
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:
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:
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:
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.
extensionNSBundle{classvar applicationVersionNumber:String{iflet version =NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"]
Como? String {return version} return "Número da versão não disponível"}
classvar applicationBuildNumber:String{iflet 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
classfunc getVersion()->String{guardlet version =Bundle.main.infoDictionary?["CFBundleShortVersionString"]as?Stringelse{return"no version info"}return version
}
Para versões mais antigas :
classfunc getVersion()->String{iflet 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;
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"]!
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.
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?overridefunc viewDidLoad(){super.viewDidLoad()// Get display name, version and build
iflet displayName =Bundle.main.localizedInfoDictionary?["CFBundleDisplayName"]as?String{self.displayName = displayName
}iflet version =Bundle.main.infoDictionary?["CFBundleShortVersionString"]as?String{self.version = version
}iflet build =Bundle.main.infoDictionary?["CFBundleVersion"]as?String{self.build = build
}}
let gAppVersion =Bundle.main.object(forInfoDictionaryKey:"CFBundleShortVersionString")??"0"let gAppBuild =Bundle.main.object(forInfoDictionaryKey:"CFBundleVersion")??"0"
importFoundationpublicextensionBundle{publicvar shortVersion:String{iflet result = infoDictionary?["CFBundleShortVersionString"]as?String{return result
}else{
assert(false)return""}}publicvar buildVersion:String{iflet result = infoDictionary?["CFBundleVersion"]as?String{return result
}else{
assert(false)return""}}publicvar fullVersion:String{return"\(shortVersion)(\(buildVersion))"}}
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:
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!Stringlet build =NSBundle.mainBundle().infoDictionary!["CFBundleVersion"]as!String
extensionUIApplication{staticvar appVersion:String{iflet appVersion =NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString"){return"\(appVersion)"}else{return""}}staticvar build:String{iflet buildVersion =NSBundle.mainBundle().objectForInfoDictionaryKey(kCFBundleVersionKey asString){return"\(buildVersion)"}else{return""}}staticvar 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.
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{returnBundle.main.infoDictionary![kCFBundleVersionKey asString]as!String}
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:
importSwifterSwiftlet buildNumber =SwifterSwift.appBuild
let version =SwifterSwift.appVersion
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:
iflet version:String=Bundle.main.infoDictionary?["CFBundleVersion"]as?String{guardlet intVersion =Int(version)else{return}ifUserDefaults.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.
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 {
publicvar appVersionShort:String?{iflet result = infoDictionary?["CFBundleShortVersionString"]as?String{return result
}else{return"⚠️"}}publicvar appVersionLong:String?{iflet result = infoDictionary?["CFBundleVersion"]as?String{return result
}else{return"⚠️"}}publicvar appName:String?{iflet result = infoDictionary?["CFBundleName"]as?String{return result
}else{return"⚠️"}}
CFBundleVersion
& CFBundleShortVersionString`. A primeira é a sua versão de compilação . O outro é o número da versão . Veja aqui para mais informaçõesRespostas:
EDITAR
Atualizado para o Swift 4.2
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!
Finalizar edição
Basta usar a mesma lógica que no Objective-C, mas com algumas pequenas alterações
Espero que isso ajude você.
David
fonte
infoDictionary
deve ser desembrulhado usando!
. Isto é o que eu estou usando, colocado em um arquivo Globals.swift:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
Eu sei que isso já foi respondido, mas pessoalmente acho que isso é um pouco mais limpo:
Swift 3.0:
Swift <2.3
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.
fonte
self.labelVersion.text
é do tipo opcional, para que você possa atribuir diretamenteNSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
let
, apenas se perguntando por que pode ser necessário. Obrigado!Atualizado para o Swift 3.0
Os
NS
prefixos-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:Resposta atualizada antiga
Resposta original
fonte
Sei também que isso já foi respondido, mas encerrei as respostas anteriores:
(*) Atualizado para extensões
Uso:
@ Deprecated: respostas antigas
Swift 3.1 :
Para versões mais antigas :
Portanto, se você deseja definir o texto do rótulo ou usar em outro lugar;
fonte
Para Swift 4.0
fonte
Eu fiz uma extensão no pacote
e depois use
fonte
Para o Swift 3.0, o NSBundle não funciona, o código a seguir funciona perfeitamente.
e apenas para o número da compilação, é:
Confusamente 'CFBundleVersion' é o número da compilação, conforme digitado no Xcode em Geral-> Identidade.
fonte
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.
fonte
Xcode 8, Swift 3:
fonte
Swift 4, Extensão útil para Bundle
fonte
Bundle + Extensions.swift
Uso:
fonte
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:
fonte
Minha resposta (em agosto de 2015), dado o Swift continuar evoluindo:
fonte
Eu criei uma extensão para UIApplication.
fonte
Tendo examinado a documentação, acredito que o seguinte é mais limpo:
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."
fonte
Para o Swift 1.2, é:
fonte
Swift 3:
Número da versão
Número da compilação
fonte
Swift 4
Sintaxe antiga rápida
fonte
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.
fonte
Aqui está uma versão atualizada do Swift 3.2:
fonte
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.
fonte
fonte
SWIFT 4
// Primeiro obtenha o nsObject definindo como um AnyObject opcional
// Em seguida, basta converter o objeto como uma String, mas tenha cuidado, verifique duas vezes se não há
fonte
para qualquer pessoa interessada, há uma biblioteca agradável e organizada chamada
SwifterSwift
disponí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:
fonte
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:
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.
fonte
Para o Swift 5.0 :
fonte
Função utilitário simples para retornar a versão do aplicativo como Int
fonte
fonte
Pacote + Extension.swift (SwiftUI, Swift 5, Xcode 11)
Combinei idéias com algumas respostas e estendi um pouco:
Fundação de importação
pacote de extensão {
}
Exemplo de uso do SwiftUI
fonte
fonte