Версия и билд приложения iOS в Swift: простое решение

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

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

Вы когда-нибудь задумывались, почему в App Store одно и то же приложение может называться «МойМессенджер 3.2», а в настройках телефона — «3.2.1 (45)»? Или почему разработчики просят прислать «номер сборки», когда вы сообщаете о баге? Ответ прост: внутри каждого iOS-приложения живут два скрытых числа — версия приложения и номер сборки. И сегодня мы научимся извлекать их с помощью Swift. Это один из самых популярных вопросов на Stack Overflow (десятки тысяч просмотров!), и не зря: знание этих двух строк кода может спасти вам часы отладки и нервов пользователей.

В этой статье мы не просто скопируем ответ с форума. Мы разберём, что такое версия и билд, зачем они нужны, как их правильно получить и, самое главное, как не наступить на грабли. Поехали!

Что такое версия (version) и номер сборки (build number)?

Прежде чем писать код, давайте разберёмся с терминами. В Xcode, в файле Info.plist (или в настройках проекта), есть два ключа:

  • CFBundleShortVersionString — это то, что пользователь видит в App Store и в настройках приложения. Обычно выглядит как «1.0», «2.3.1» и т.д. Это версия для пользователя.
  • CFBundleVersion — это внутренний номер сборки. Он может быть любым: «42», «20240315», «1.0.0.123». Это версия для разработчика, которая помогает отличать разные сборки одной и той же версии.

Зачем разделять? Представьте, что вы выпустили версию 1.0, но нашли критический баг. Вы исправляете его, не меняя функционала — это всё ещё версия 1.0 для пользователя, но номер сборки вы увеличиваете (например, с 1 на 2). Так вы и пользователя не путаете, и в Crashlytics видите, какая именно сборка упала.

Самый простой способ: Bundle.main

Apple предоставляет удобный доступ к этим данным через объект Bundle.main. Вот классическое решение, которое вы найдёте на Stack Overflow:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Получаем версию приложения
        let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
        // Получаем номер сборки
        let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
        
        print("Версия приложения: \(appVersion)")
        print("Номер сборки: \(buildNumber)")
        
        // Пример использования в UI
        versionLabel.text = "Версия \(appVersion) (сборка \(buildNumber))"
    }
}

Всё гениальное просто. Но давайте разберём каждую строчку:

  • Bundle.main — это главный бандл вашего приложения (файл .app).
  • infoDictionary — словарь, который читает Info.plist.
  • Мы используем опциональное приведение через as? String и оператор ?? на случай, если ключа вдруг нет (хотя это маловероятно).

Продвинутый подход: читаем из ProcessInfo

Хотите выглядеть как профи? Используйте ProcessInfo. Этот класс знает о системе больше, чем просто plist. Но для версии приложения он не даёт ничего нового, зато может показать операционную систему. Однако есть один трюк: если вы оборачиваете получение данных в extension, код становится чище и переиспользуемее.

import Foundation

extension Bundle {
    var appVersion: String {
        return infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
    }
    
    var buildNumber: String {
        return infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
    }
    
    var fullVersion: String {
        return "Версия \(appVersion) (сборка \(buildNumber))"
    }
}

// Использование
print(Bundle.main.fullVersion) // "Версия 1.0 (сборка 42)"

Этот подход особенно полезен, если вы выводите версию в нескольких местах (например, в настройках, в алерте о баге, в логах). Не нужно каждый раз писать одну и ту же цепочку — просто вызывайте Bundle.main.fullVersion.

А что если я использую SwiftUI?

Для SwiftUI код практически не меняется. Вы всё так же обращаетесь к Bundle.main. Единственное отличие — вы можете использовать @State или вычисляемое свойство для реактивности:

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Приложение")
                .font(.title)
            Text("Версия: \(Bundle.main.appVersion)")
            Text("Сборка: \(Bundle.main.buildNumber)")
        }
        .padding()
    }
}

// Не забудьте про extension, который мы написали выше!

Обратите внимание: Bundle.main не является ObservableObject, поэтому если вы динамически меняете версию (что вряд ли), нужно будет обновлять экран вручную. Но обычно версия зашита в билд и не меняется во время работы приложения.

Почему это может не сработать? Типичные ошибки

На Stack Overflow под этим вопросом — сотни комментариев от разработчиков, у которых код не работал. Вот три главные причины:

  1. Вы обращаетесь к неправильному бандлу. Если вы пишете библиотеку или расширение (Siri Intent, Widget), у вас может быть свой бандл. В этом случае используйте Bundle(for: type(of: self)) или передайте нужный бандл явно.
  2. Ключи в Info.plist переименованы. Иногда разработчики (или автоматические скрипты) меняют ключи. Проверьте, что у вас есть CFBundleShortVersionString и CFBundleVersion в вашем Info.plist.
  3. Вы используете симулятор с тестовым билдом. На симуляторе всё работает, но на реальном устройстве — нет? Проверьте схему сборки (Scheme). Иногда Xcode генерирует временный Info.plist без этих ключей.

Чтобы отловить такие ошибки, добавьте проверку:

guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
    fatalError("Info.plist does not contain CFBundleShortVersionString!")
}

Но в продакшене лучше не использовать fatalError — просто покажите "Unknown".

Зачем это нужно в реальном проекте?

Теперь, когда вы умеете получать версию и билд, давайте подумаем, где это применять. Вот три killer-фичи:

  • Экран «О приложении» — пользователи любят видеть версию. Это создаёт доверие и помогает в поддержке.
  • Отправка в аналитику — при каждом запуске отправляйте версию и билд в Firebase или Amplitude. Тогда вы точно будете знать, в какой сборке произ

Блоги

Рекомендаций по книгам