Jak uzyskać wersję aplikacji i numer kompilacji za pomocą Swift?


386

Mam aplikację IOS z zapleczem platformy Azure i chciałbym rejestrować pewne zdarzenia, takie jak dane logowania i które wersje użytkowników aplikacji są uruchomione.

Jak mogę zwrócić wersję i numer kompilacji za pomocą Swift?



6
To Cel C, a nie Szybki.
Øyvind Vik

10
Pamiętaj, aby nie pomylić CFBundleVersioni CFBundleShortVersionString`. Pierwsza to twoja wersja kompilacji . Drugi to numer wersji . Zobacz tutaj, aby uzyskać więcej informacji
Honey

1
@ ØyvindVik Większość osób zakłada, że ​​możesz przetłumaczyć rozwiązanie Objective-C na Swift bez trzymania ręki.
gnasher729,

Odpowiedzi:


424

EDYTOWAĆ

Zaktualizowano dla Swift 4.2

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

EDYTOWAĆ

Jak zauważył @azdev w nowej wersji Xcode, otrzymasz błąd kompilacji podczas wypróbowania mojego poprzedniego rozwiązania. Aby rozwiązać ten problem, po prostu edytuj go zgodnie z sugestią, aby rozpakować słownik pakietów za pomocą!

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

Zakończ edycję

Po prostu użyj tej samej logiki, co w Objective-C, ale z pewnymi drobnymi zmianami

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

Mam nadzieję, że to ci pomoże.

David


Gdy go używam, pojawia się błąd kompilatora „[NSObject: AnyObject]? Nie ma elementu o nazwie„ indeks dolny ””
andreas

Możesz spróbować zastąpić AnyObject? przez AnyObject! nie wiedząc dokładnie, jakiego kodu używasz, bardzo trudno zgadnąć, co jest nie tak, pamiętaj też, że Swift może zmieniać się z jednej wersji Xcode na inną, więc znajomość Twojej wersji Xcode będzie również istotne.
David

18
@andreas infoDictionarynależy rozpakować za pomocą !. Właśnie tego używam, umieszczonego w pliku Globals.swift:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
azdev,

1
Musiałem dodać jeszcze jeden „!” po „as”. let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
yosei

3
Należy unikać używania wymuszonego odwijania „!” ponieważ powodują awarię aplikacji, gdy jedna z tych wartości jest zerowa
Julius

278

Wiem, że już na nie odpowiedziano, ale osobiście uważam, że jest to trochę czystsze:

Swift 3.0:

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

Szybki <2.3

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

W ten sposób wersja if let zajmuje się przetwarzaniem warunkowym (ustawienie tekstu etykiety w moim przypadku), a jeśli infoDictionary lub CFBundleShortVersionString są zerowe, opcjonalne rozpakowanie spowoduje pominięcie kodu.


6
self.labelVersion.textjest typem opcjonalnym, więc możesz przypisać bezpośrednioNSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
Jonauz

8
Czy istnieje powód, dla którego wartość nie zostanie ustawiona? Zgadzam się, że jest zdecydowanie bardziej ostrożny let, tylko zastanawiam się, dlaczego może być potrzebny. Dzięki!
Crashalot

@Crashalot pomimo twojego imienia;) nie chcesz, aby twoja aplikacja uległa awarii, jeśli, powiedzmy, robisz literówkę, raczej numer wersji to „coś poszło nie tak”.
Daniel Springer,

OP: możesz wymienić? z ! i usuń „as String”. Jeśli jest zero, to i tak nie ulegnie awarii
Daniel Springer,

245

Zaktualizowano dla Swift 3.0

W NS-prefixes są teraz poszedł w Swift 3.0 i kilka właściwości / metody zostały zmienione nazwy być bardziej Swifty. Oto jak to teraz wygląda:

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

Bundle.main.releaseVersionNumber
Bundle.main.buildVersionNumber

Stara zaktualizowana odpowiedź

Od czasu mojej pierwotnej odpowiedzi dużo pracuję z Frameworkami, dlatego chciałem zaktualizować swoje rozwiązanie do czegoś, co jest zarówno prostsze, jak i znacznie bardziej przydatne w środowisku obejmującym wiele pakietów:

extension NSBundle {

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

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

}

Teraz to rozszerzenie będzie przydatne w aplikacjach do identyfikacji zarówno głównego pakietu, jak i wszelkich innych zawartych pakietów (takich jak wspólny framework do programowania rozszerzeń lub trzecie frameworki, takie jak AFNetworking):

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

// or...

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

Oryginalna odpowiedź

Chciałem poprawić niektóre z już opublikowanych odpowiedzi. Napisałem rozszerzenie klasy, które można dodać do łańcucha narzędzi, aby obsłużyć to w bardziej logiczny sposób.

extension NSBundle {

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

tak jak? Ciąg {return version} return „Numer wersji niedostępny”}

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

}

Teraz możesz uzyskać do niego łatwy dostęp poprzez:

let versionNumber = NSBundle.applicationVersionNumber

CFBundleVersionKey nie działa już w Swift 3, Xcode 8. Czy znasz nowy klucz do użycia?
Crashalot,

71

Wiem też, że już na nie odpowiedziano, ale podsumowałem poprzednie odpowiedzi:

(*) Zaktualizowano dla rozszerzeń

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

Stosowanie:

someLabel.text = Bundle.main.releaseVersionNumberPretty

@Deprecated: Stare odpowiedzi

Swift 3.1 :

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

W przypadku starszych wersji :

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

Więc jeśli chcesz ustawić tekst etykiety lub chcesz użyć gdzie indziej;

self.labelVersion.text = getVersion()

1
lub: class func getVersion () -> String {return NSBundle.mainBundle (). infoDictionary? ["CFBundleShortVersionString"] as? Strunowy ?? „brak informacji o wersji”}
tapmonkey

Myślę, że kopiowanie innych odpowiedzi nie ma sensu. Jeśli twoja odpowiedź nie jest już ważna, zawsze masz możliwość jej usunięcia i zrobienia miejsca dla innych odpowiedzi :)
carmen_munich 18.04.19

1
@carmen_munich Ponieważ oszczerstwo tutaj muszę ci odpowiedzieć. Przede wszystkim ta odpowiedź została opublikowana w marcu 2015 r., A odpowiedź w lutym 2017 r. Dlatego inspiracja musi pochodzić z wcześniejszych odpowiedzi. Po drugie, w ogóle nie widziałem twojej odpowiedzi, zaktualizowałem swoją odpowiedź, ponieważ obecnie używam jej w ten sposób. Wydaje mi się, że użycie rozszerzenia nie jest wyjątkowe dla kogoś ze społeczności iOS. Naprawdę staraj się być dojrzałym i szanować innych programistów. Nic tu nie publikuję. Chciałbym pomóc ludziom, to wszystko. Staraj się nie zniechęcać ludzi próbujących pomóc w SO.
Gunhan

Słyszałem wiele opinii od początkujących, że publikują odpowiedzi i chcą zobaczyć, że ktoś klika „up”, co jest naprawdę miłe i motywuje ludzi. Ale jeśli ktoś skopiuje odpowiedzi w swojej nieaktualnej odpowiedzi, ten, który podjął wysiłek opublikowania odpowiedzi, nie uzyska takiej motywacji, że ktoś ją przegłosował. Tak więc nowicjusze naprawdę odczuwają rozczarowanie i mają wrażenie, że nie wnoszą wartości dla społeczności i przestają publikować. I nie zrozumcie tego źle, mam na myśli to ogólnie. Mam nadzieję, że nie czujesz się obrażony i lepiej rozumiesz teraz, dlaczego przedstawiłem tę sugestię.
carmen_munich

@carmen_munich Jeśli uporządkujesz chronologicznie odpowiedzi na to pytanie, zauważysz, że ktoś inny już udzielił tej samej odpowiedzi, co przed tobą! Więc obwiniasz mnie za coś, co sam zrobiłeś. Ponieważ mam historię tego pytania, podzieliłem się nowymi preferencjami użytkowania w aktualizacji. To wszystko.
Gunhan

32

Dla Swift 4.0

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

28

Zrobiłem rozszerzenie w pakiecie

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
    }

}

a następnie użyj go

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

Właściwie to trochę inaczej. Używam na przykład rozpakowywania wymuszonego. Możesz pomyśleć, że wymuszanie rozpakowywania jest złe, to jeden z rzadkich przypadków, w których jest w porządku. Aby wyjaśnić to nieco bardziej, wartości te powinny znajdować się w słowniku, w przeciwnym razie coś jest naprawdę nie tak z plikiem projektu. Dlatego w tym podejściu stosuje się rozpakowywanie wymuszone :-)
carmen_munich 24.04.19

Zmiana nazwy i wymuszenie rozpakowywania nie jest zmianą w opublikowaniu jako nowej odpowiedzi. Ponadto ludzie mogą dowiedzieć się, że wymuszenie rozpakowywania może być używane w dowolnym miejscu, gdy zobaczą twoją odpowiedź. Dla czytelników nie zakładaj, że klucz tam jest, zawsze lepiej jest ręcznie obsługiwać opcjonalne rozpakowywanie niż wymuszać rozpakowywanie.
Gunhan

Aby być uczciwym, istnieją rzadkie przypadki, w których wymuszanie rozpakowywania jest w porządku. Ten jeden post pokazuje tylko jeden przypadek, w którym może być w porządku. Jeśli chcesz dowiedzieć się więcej o przypadkach rozpakowywania siły, znajdziesz kilka dobrych wyjaśnień i samouczków od Paula Hudsona. Naprawdę mogę polecić wszystkim początkującym www.hackingwithswift.com
carmen_munich 24.04.2019

To dobra rekomendacja dla początkujących. Może możesz także przeczytać więcej i dowiedzieć się więcej. Powinieneś także poprawić swoje rozumienie komentarzy i ich znaczenia. Np. Nikt nie powiedział ci, że nigdy / nigdy nie używaj wymuszania rozpakowywania. Ale w przypadku dyktafonu informacyjnego klucze te można usunąć, a siła odwijania może spowodować awarię. Bezpieczniej jest tutaj rozpakowywać.
Gunhan

Może możesz wyjaśnić, w którym przypadku można je usunąć i być zerowe? Czego nauczyłem się z wspomnianego zasobu, nie ma go w tym przypadku partiuclare. Powinny zawsze tam być, chyba że plik projektu jest uszkodzony, w takim przypadku projekt prawdopodobnie i tak nie skompilowałby się
carmen_munich 24.04.19

16

W przypadku Swift 3.0 NSBundle nie działa, następujący kod działa idealnie.

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

a dla samego numeru kompilacji jest to:

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

Myląco „CFBundleVersion” to numer kompilacji wprowadzony w Xcode na Ogólne-> Tożsamość.


15

Xcode 9.4.1 Swift 4.1

Zwróć uwagę na użycie localizedInfoDictionary do wybrania odpowiedniej wersji językowej wyświetlanej nazwy pakietu.

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"

12

Swift 4, przydatne rozszerzenie pakietu

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

Aby tego użyć, powiedz na przykład Bundle.main.fullVersion
Joseph Astrahan

11

Pakiet + Rozszerzenia.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
    }
}

Stosowanie:

someLabel.text = Bundle.main.versionNumber

11

OP poprosił o numer wersji i numer kompilacji. Niestety większość odpowiedzi nie zapewnia obu tych opcji. Ponadto inni dodają niepotrzebne metody rozszerzenia. Oto jeden, który jest dość prosty i rozwiązuje problem 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

Moja odpowiedź (z sierpnia 2015 r.), Biorąc pod uwagę Swift, ewoluuje:

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

8

Stworzyłem rozszerzenie dla 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

Po przejrzeniu dokumentacji uważam, że następujące elementy są czystsze:

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

Źródło : „Zastosowanie tej metody jest lepsze niż innych metod dostępu, ponieważ zwraca zlokalizowaną wartość klucza, gdy jest on dostępny.”


1
to jest właściwa Szybka droga, bez rozpakowywania siły, nigdzie
Juan Boero

5

W przypadku Swift 1.2 jest to:

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

1
możesz użyć? także
Dan Rosenstark,

4

Swift 3:

Numer wersji

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

Numer kompilacji

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

Co powiesz na numer kompilacji? Dzięki! CFBundleVersionKey nie działa.
Crashalot,

@ Crashalot Zaktualizowałem go również numerem kompilacji. Oto także lista wszystkich podstawowych kluczy podstawowych: developer.apple.com/library/content/documentation/General/...
jasonnoahchoi

3

Szybki 4

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

Bundle.main.infoDictionary! ["CFBundleShortVersionString"]

Szybka stara składnia

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
    }

}

Uwaga: Powinieneś użyć tej opcji, jeśli tutaj, jeśli wersja aplikacji lub kompilacja nie jest ustawiona, co spowoduje awarię, jeśli spróbujesz użyć! rozpakować.


2

Oto zaktualizowana wersja 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

Możesz teraz użyć do tego stałej, zamiast konieczności używania kodu o ciągach ciągłych, jak poprzednio, co sprawia, że ​​rzeczy są jeszcze wygodniejsze.

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

// Najpierw pobierz nsObject, definiując jako opcjonalny AnyObject

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

// Następnie po prostu rzuć obiekt jako Ciąg, ale bądź ostrożny, możesz chcieć dwukrotnie sprawdzić zero

let version = nsObject as! String

1

Prosta funkcja narzędzia do zwracania wersji aplikacji jako 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
        }

0

dla wszystkich zainteresowanych dostępna jest ładna i schludna biblioteka o nazwie SwifterSwiftdostępna na github, a także w pełni udokumentowana dla każdej wersji swift (patrz swifterswift.com ).

przy użyciu tej biblioteki odczytanie wersji aplikacji i numeru kompilacji byłoby tak proste, jak to:

import SwifterSwift

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

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

    }

0

Aktualizacja dla Swift 5

Oto funkcja, której używam, aby zdecydować, czy wyświetlić stronę „Aktualizacja aplikacji”, czy nie. Zwraca numer kompilacji, którą konwertuję na 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")
    }

Jeśli nigdy wcześniej nie zostanie użyty, zwróci 0, czyli mniej niż bieżący numer kompilacji. Aby nie wyświetlać takiego ekranu nowym użytkownikom, wystarczy dodać numer kompilacji po pierwszym logowaniu lub po zakończeniu wbudowania.


0

W przypadku Swift 5.0 :

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

0

Swift 5 jako rozszerzenie UIApplication

extension UIApplication {
    static var release: String {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String? ?? "x.x"
    }
    static var build: String {
        return Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as! String? ?? "x"
    }
    static var version: String {
        return "\(release).\(build)"
    }
}

przykład użycia:

print("release: \(UIApplication.release)")
print("build: \(UIApplication.build)")
print("version: \(UIApplication.version)")
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.