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?
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 anyObjectlet nsObject:AnyObject?=NSBundle.mainBundle().infoDictionary["CFBundleShortVersionString"]//Then just cast the object as a String, but be careful, you may want to double check for nillet version = nsObject as!String
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:
iflet version =Bundle.main.infoDictionary?["CFBundleShortVersionString"]as?String{self.labelVersion.text = version
}
Szybki <2.3
iflet 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.
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:
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:
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):
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.
extensionNSBundle{classvar applicationVersionNumber:String{iflet version =NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"]
tak jak? Ciąg {return version} return „Numer wersji niedostępny”}
classvar applicationBuildNumber:String{iflet 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
classfunc getVersion()->String{guardlet version =Bundle.main.infoDictionary?["CFBundleShortVersionString"]as?Stringelse{return"no version info"}return version
}
W przypadku starszych wersji :
classfunc getVersion()->String{iflet 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;
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"]!
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ść.
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?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))"}}
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:
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!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
}}
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ć.
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{returnBundle.main.infoDictionary![kCFBundleVersionKey asString]as!String}
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:
importSwifterSwiftlet buildNumber =SwifterSwift.appBuild
let version =SwifterSwift.appVersion
Oto funkcja, której używam, aby zdecydować, czy wyświetlić stronę „Aktualizacja aplikacji”, czy nie. Zwraca numer kompilacji, którą konwertuję na 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")}
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.
CFBundleVersion
i CFBundleShortVersionString`. Pierwsza to twoja wersja kompilacji . Drugi to numer wersji . Zobacz tutaj, aby uzyskać więcej informacjiOdpowiedzi:
EDYTOWAĆ
Zaktualizowano dla Swift 4.2
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ą!
Zakończ edycję
Po prostu użyj tej samej logiki, co w Objective-C, ale z pewnymi drobnymi zmianami
Mam nadzieję, że to ci pomoże.
David
źródło
infoDictionary
należy rozpakować za pomocą!
. Właśnie tego używam, umieszczonego w pliku Globals.swift:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
Wiem, że już na nie odpowiedziano, ale osobiście uważam, że jest to trochę czystsze:
Swift 3.0:
Szybki <2.3
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.
źródło
self.labelVersion.text
jest typem opcjonalnym, więc możesz przypisać bezpośrednioNSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
let
, tylko zastanawiam się, dlaczego może być potrzebny. Dzięki!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:Stara zaktualizowana odpowiedź
Oryginalna odpowiedź
źródło
Wiem też, że już na nie odpowiedziano, ale podsumowałem poprzednie odpowiedzi:
(*) Zaktualizowano dla rozszerzeń
Stosowanie:
@Deprecated: Stare odpowiedzi
Swift 3.1 :
W przypadku starszych wersji :
Więc jeśli chcesz ustawić tekst etykiety lub chcesz użyć gdzie indziej;
źródło
Dla Swift 4.0
źródło
Zrobiłem rozszerzenie w pakiecie
a następnie użyj go
źródło
W przypadku Swift 3.0 NSBundle nie działa, następujący kod działa idealnie.
a dla samego numeru kompilacji jest to:
Myląco „CFBundleVersion” to numer kompilacji wprowadzony w Xcode na Ogólne-> Tożsamość.
źródło
Xcode 9.4.1 Swift 4.1
Zwróć uwagę na użycie localizedInfoDictionary do wybrania odpowiedniej wersji językowej wyświetlanej nazwy pakietu.
źródło
Xcode 8, Swift 3:
źródło
Swift 4, przydatne rozszerzenie pakietu
źródło
Pakiet + Rozszerzenia.swift
Stosowanie:
źródło
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:
źródło
Moja odpowiedź (z sierpnia 2015 r.), Biorąc pod uwagę Swift, ewoluuje:
źródło
Stworzyłem rozszerzenie dla UIApplication.
źródło
Po przejrzeniu dokumentacji uważam, że następujące elementy są czystsze:
Źródło : „Zastosowanie tej metody jest lepsze niż innych metod dostępu, ponieważ zwraca zlokalizowaną wartość klucza, gdy jest on dostępny.”
źródło
W przypadku Swift 1.2 jest to:
źródło
Swift 3:
Numer wersji
Numer kompilacji
źródło
Szybki 4
Szybka stara składnia
źródło
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ć.
źródło
Oto zaktualizowana wersja Swift 3.2:
źródło
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.
źródło
źródło
SWIFT 4
// Najpierw pobierz nsObject, definiując jako opcjonalny AnyObject
// Następnie po prostu rzuć obiekt jako Ciąg, ale bądź ostrożny, możesz chcieć dwukrotnie sprawdzić zero
źródło
Prosta funkcja narzędzia do zwracania wersji aplikacji jako Int
źródło
źródło
dla wszystkich zainteresowanych dostępna jest ładna i schludna biblioteka o nazwie
SwifterSwift
dostę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:
źródło
źródło
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:
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.
źródło
W przypadku Swift 5.0 :
źródło
Swift 5 jako rozszerzenie UIApplication
przykład użycia:
źródło