Как получить версию приложения и номер сборки с помощью Swift?


389

У меня есть приложение IOS с серверной частью Azure, и я хотел бы регистрировать определенные события, например, имена входа и какие версии пользователей приложения работают.

Как я могу вернуть версию и номер сборки, используя Swift?



6
Это Objective-C, а не Swift.
Ойвинд Вик

10
Обязательно не путайте CFBundleVersion& CFBundleShortVersionString`. Первая - это ваша версия сборки . Другой номер версии . Смотрите здесь для получения дополнительной информации
Мед

1
@ ØyvindVik Большинство людей предполагают, что вы можете перевести решение Objective-C на Swift, не взявшись за руки.
gnasher729

Ответы:


425

РЕДАКТИРОВАТЬ

Обновлено для Swift 4.2

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

РЕДАКТИРОВАТЬ

Как указал @azdev в новой версии Xcode, вы получите ошибку компиляции при попытке использовать мое предыдущее решение, чтобы решить эту проблему, просто отредактируйте его, как предложено, чтобы развернуть словарь пакета, используя!

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

Конец Править

Просто используйте ту же логику, что и в Objective-C, но с небольшими изменениями

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

Я надеюсь, что это помогает вам.

Дэвид


Когда я использую это, я получаю ошибку компилятора «[NSObject: AnyObject]? Не имеет члена с именем« subscript »»
andreas

Вы можете попробовать заменить AnyObject? AnyObject! не зная точно, какой код вы используете, очень трудно угадать, что не так, также имейте в виду, что Swift может переходить с одного релиза Xcode на другой, поэтому также важно знать вашу версию Xcode.
Дэвид

18
@andreas infoDictionaryдолжен быть развернут с помощью !. Это то, что я использую, помещено в файл Globals.swift:let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as String
azdev

1
Я должен был добавить еще один "!" после "как". let appVersion = NSBundle.mainBundle().infoDictionary!["CFBundleVersion"] as! String
Йосей

3
Вы должны избегать использования принудительной развертки "!" так как они приведут к сбою вашего приложения всякий раз, когда одно из этих значений будет равно нулю
Julius

279

Я знаю, что на это уже ответили, но лично я думаю, что это немного чище:

Swift 3.0:

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

Свифт <2.3

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

Таким образом, версия if let заботится об условной обработке (в моем случае задает текст метки), и если infoDictionary или CFBundleShortVersionString равны нулю, необязательное развертывание приведет к пропуску кода.


6
self.labelVersion.textявляется необязательным типом, так что вы можете напрямую назначитьNSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
Jonauz

8
Есть ли причина, по которой значение не будет установлено? Согласитесь, с ним определенно более осторожно let, просто интересно, зачем это нужно. Спасибо!
Crashalot

@Crashalot несмотря на ваше имя;) вы не хотите, чтобы ваше приложение зависало, если, скажем, вы сделали опечатку, а номер версии должен быть «что-то пошло не так».
Даниэль Спрингер

ОП: вы можете заменить? с ! и удалите "как строку". Если это ноль, это в любом случае не будет терпеть крах
Даниэль Спрингер

245

Обновлено для Swift 3.0

В NS-prefixes теперь ушли в Swift 3.0 и некоторые свойства / методы изменились имена более Swifty. Вот как это выглядит сейчас:

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

Bundle.main.releaseVersionNumber
Bundle.main.buildVersionNumber

Старый обновленный ответ

Со времени моего первоначального ответа я много работал с Frameworks, поэтому я хотел обновить свое решение до чего-то более простого и гораздо более полезного в среде с несколькими пакетами:

extension NSBundle {

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

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

}

Теперь это расширение будет полезно в приложениях для идентификации как основного пакета, так и любых других включенных пакетов (таких как общая платформа для программирования расширений или третьи платформы, такие как AFNetworking), например, так:

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

// or...

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

Оригинальный ответ

Я хотел бы улучшить некоторые из ответов, уже опубликованных. Я написал расширение класса, которое может быть добавлено в вашу цепочку инструментов для более логичной обработки.

extension NSBundle {

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

в виде? String {return version} return "Номер версии недоступен"}

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

}

Так что теперь вы можете легко получить к нему доступ:

let versionNumber = NSBundle.applicationVersionNumber

CFBundleVersionKey больше не работает в Swift 3, Xcode 8. Знаете ли вы новый ключ для использования?
Crashalot

71

Я также знаю, что на это уже ответили, но я обернул предыдущие ответы:

(*) Обновлено для расширений

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

Применение:

someLabel.text = Bundle.main.releaseVersionNumberPretty

@ Устарел: старые ответы

Swift 3.1 :

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

Для более старых версий :

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

Так что если вы хотите установить текст метки или хотите использовать где-то еще;

self.labelVersion.text = getVersion()

1
или: класс func getVersion () -> String {return NSBundle.mainBundle (). infoDictionary? ["CFBundleShortVersionString"] как? Строка ?? "нет информации о версии"}
tapmonkey

Я думаю, копирование других ответов не имеет смысла. Если ваш ответ больше не действителен, тогда у вас всегда есть возможность удалить его и освободить место для других ответов :)
carmen_munich

1
@carmen_munich Так как ты клевещешь здесь, я должен ответить тебе. Прежде всего, этот ответ опубликован в марте 2015 года, а ваш ответ опубликован в феврале 2017 года. Поэтому ваше вдохновение должно исходить из предыдущих ответов. Во-вторых, я не видел ваш ответ вообще, я обновил свой ответ, потому что я использую его таким образом в настоящее время. Полагаю, использование расширения не является уникальным для кого-то из сообщества iOS. На самом деле, пожалуйста, постарайтесь быть зрелыми и уважать других разработчиков. Я ничего не пишу здесь. Я хотел бы помочь людям, вот и все. Пожалуйста, постарайтесь не отговаривать людей, пытающихся помочь на SO.
Ганхан

Я слышал множество отзывов от новичков о том, что они публикуют ответ и хотят видеть, что кто-то нажимает «вверх», что действительно приятно видеть и мотивирует людей. Но если кто-то скопирует ответы в своем устаревшем ответе, тот, кто попытался опубликовать его, не получит мотивацию, что кто-то проголосовал за него. Таким образом, новички действительно испытывают разочарование и чувствуют, что не приносят пользы сообществу и не прекращают публикацию. И не поймите неправильно, я имею в виду это в целом. Надеюсь, вы не обижаетесь и теперь не понимаете, почему я сделал это предложение.
carmen_munich

@carmen_munich Если вы упорядочите ответы на этот вопрос в хронологическом порядке, вы заметите, что кто-то уже дал тот же ответ, что и вы раньше! Так ты обвиняешь меня в том, что сделал сам. Поскольку у меня есть история для этого вопроса, я поделился своими новыми предпочтениями использования в обновлении. Это все.
Ганхан,


29

Я сделал расширение на Bundle

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
    }

}

а затем использовать его

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

На самом деле это немного по-другому. Я использую распаковку, например. Вы можете подумать, что принудительное распаковывание вообще плохо, это один из редких случаев, когда это нормально. Чтобы объяснить это немного, эти значения должны быть в словаре, иначе что-то действительно не так с файлом проекта. Вот почему этот подход использует
распаковку

Переименование и принудительное развертывание - это не изменение в качестве нового ответа. Кроме того, люди могут узнать, что распаковка может быть использована где угодно, когда они увидят ваш ответ. Для читателей, не думайте, что ключ есть, всегда лучше обрабатывать необязательное развертывание вручную, чем принудительное развертывание.
Ганхан

Чтобы быть справедливым, есть несколько редких случаев, когда принудительное развертывание в порядке. Этот пост только показывает один случай, когда это может быть хорошо. Если вы хотите больше узнать о случаях распаковки, есть несколько хороших объяснений и учебных пособий от Пола Хадсона. Я действительно могу порекомендовать всем новичкам www.hackingwithswift.com
carmen_munich

Это хорошая рекомендация для новичков. Может быть, вы также можете прочитать больше и узнать больше. Также вы должны улучшить свое понимание комментариев и того, что они подразумевают. Например, никто не говорил вам никогда / никогда не использовать распаковку силой. Но для информации, эти ключи могут быть удалены, а принудительное развертывание может привести к сбою. Развертывание здесь безопаснее.
Ганхан

Может быть, вы можете объяснить, в каком случае они могут быть удалены и ноль? То, что я узнал из упомянутого ресурса, не в этом особом случае. Они всегда должны быть там, если файл проекта не поврежден, в этом случае проект, вероятно, не будет компилироваться в любом случае
carmen_munich

16

Для Swift 3.0 NSBundle не работает, следующий код работает отлично.

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

и только для номера сборки это:

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

Смущает, что 'CFBundleVersion' - это номер сборки, введенный в Xcode в General-> Identity.


16

Xcode 9.4.1 Swift 4.1

Обратите внимание на использование localizedInfoDictionary для выбора правильной языковой версии отображаемого имени пакета.

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"

13

Swift 4, полезное расширение для 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))"
    }
}

Чтобы использовать это, вы должны сказать, например, Bundle.main.fullVersion
Джозеф Астрахан

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

Применение:

someLabel.text = Bundle.main.versionNumber

11

ОП запросил как номер версии, так и номер сборки. К сожалению, большинство ответов не предоставляют оба этих варианта. Кроме того, другие добавляют ненужные методы расширения. Вот тот, который довольно прост и решает проблему ОП:

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

Мой ответ (по состоянию на август 2015 года), учитывая, что Свифт продолжает развиваться:

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

8

Я создал расширение для 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

Посмотрев на документацию, я считаю, что следующее чище:

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

Источник : «Использование этого метода предпочтительнее других методов доступа, потому что он возвращает локализованное значение ключа, когда он доступен».


1
это верный путь Свифта, без поворота силы, где угодно
Хуан Боэро,


4

Свифт 3:

Номер версии

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

Номер сборки

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

Как насчет номера сборки? Спасибо! CFBundleVersionKey не работает.
Crashalot

@Crashalot Я также обновил номер сборки. Вот также список всех основных ключей ядра: developer.apple.com/library/content/documentation/General/…
jasonnoahchoi

3

Swift 4

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

Bundle.main.infoDictionary! [ "CFBundleShortVersionString"]

Быстрый старый синтаксис

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
    }

}

Внимание: вы должны использовать if let здесь, если не установлена ​​версия или сборка приложения, что приведет к падению, если вы попытаетесь использовать! развернуть.


2

Вот обновленная версия для 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

Теперь вы можете использовать константу для этого, вместо того, чтобы использовать строковый код, как раньше, что делает вещи еще более удобными.

var appVersion: String {
    return Bundle.main.infoDictionary![kCFBundleVersionKey as String] as! String
}


1

SWIFT 4

// Сначала получаем nsObject, определяя как необязательный AnyObject

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

// Затем просто приведем объект в виде String, но будьте осторожны, вы можете перепроверить ноль

let version = nsObject as! String

1

для всех, кто интересуется, есть хорошая и аккуратная библиотека, SwifterSwiftдоступная на github, а также полностью документированная для каждой версии swift (см. swifterswift.com ).

Используя эту библиотеку, читать версию приложения и номер сборки так же просто, как это:

import SwifterSwift

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

1

Обновление для Swift 5

вот функция, которую я использую, чтобы решить, показывать ли страницу "приложение обновлено" или нет. Он возвращает номер сборки, который я преобразую в 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")
    }

Если никогда не использовался раньше, он вернет 0, который меньше текущего номера сборки. Чтобы не показывать такой экран новым пользователям, просто добавьте номер сборки после первого входа в систему или после завершения регистрации.


1

Для Swift 5.0 :

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

1

Простая служебная функция для возврата версии приложения как 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
        }

1

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

Я объединил идеи из нескольких ответов и немного расширил:

  • пример SwiftUI
  • Отображает смайлик с предупреждающим треугольником (а не сбой приложения), если ключ отсутствует в Info.plist

Фонд импорта

Bundle extension {

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

}


SwiftUI пример использования

VStack {

     Text("Version: \(Bundle.main.appVersionShort!) (\(Bundle.main.appVersionLong!))")
                    .font(.subheadline)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
}

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.