Avatar ·

Как проверить версию операционной системы iOS/macOS в Swift?

Всем привет! Я новичок в Swift и пишу приложение для iOS и macOS. Мне нужно, чтобы некоторые функции работали только на определённых версиях ОС. Например, я хочу использовать `UIScene` на iOS 13+, но не хочу, чтобы приложение падало на старых устройствах. Проблема в том, что я не знаю, как правильно проверить версию системы. Сначала я попробовал что-то вроде этого: ``` if UIDevice.current.systemVersion >= "13.0" { // код для iOS 13+ } ``` Но это не сработало — компилятор ругается на сравнение строк. Потом я нашёл в интернете вариант с `ProcessInfo`, но он кажется слишком сложным: ``` let osVersion = ProcessInfo.processInfo.operatingSystemVersion if osVersion.majorVersion >= 13 { // ... } ``` Работает, но мне не нравится, что приходится разбивать версию на части. Есть ли более простой и надёжный способ? Может быть, есть какой-то встроенный макрос или функция для проверки версии ОС? Я слышал про `#available`, но не до конца понимаю, как его использовать. Подскажите, пожалуйста, как правильно это сделать в современном Swift (Xcode 14+). Заранее спасибо! <<>>

Использование #available — самый правильный способ в Swift

Вы на верном пути! Действительно, сравнивать строки через UIDevice.current.systemVersion — плохая практика, так как это ненадёжно и может привести к ошибкам. В Swift существует элегантное решение — директива #available. Она позволяет проверить версию ОС на этапе выполнения (runtime) и при этом даёт подсказки компилятору.

Вот как это работает:

if #available(iOS 13, *) {
    // Этот код выполнится только на iOS 13 и новее
    let scene = UIScene()
} else {
    // Этот код для более старых версий (iOS 12 и ниже)
    // Здесь можно использовать старый API
}

Звёздочка * означает «для всех остальных платформ» (macOS, watchOS, tvOS). Вы можете указать несколько платформ:

if #available(iOS 13, macOS 10.15, *) {
    // Код для iOS 13+ и macOS 10.15+
}

Почему это лучше, чем сравнение строк?

  • Безопасность типов: компилятор проверяет корректность версий.
  • Читаемость: код выглядит как обычное условие.
  • Интеграция с Xcode: если вы используете API, доступный только с определённой версии, Xcode предложит обернуть его в #available.
  • Поддержка всех платформ Apple: работает для iOS, macOS, watchOS, tvOS и даже для Catalyst.

Альтернатива: ProcessInfo

Ваш вариант с ProcessInfo тоже рабочий, но он менее удобен. Вот как его можно улучшить:

let osVersion = ProcessInfo.processInfo.operatingSystemVersion
if osVersion.majorVersion >= 13 {
    // iOS 13+
} else if osVersion.majorVersion == 12 && osVersion.minorVersion >= 4 {
    // iOS 12.4+
}

Но для простых проверок #available предпочтительнее.

Хитрости и частые ошибки

  • Не путайте #available с @available: #available — это условие (if), а @available — атрибут для функций/классов. Например, чтобы пометить метод как доступный только с iOS 13:
@available(iOS 13, *)
func newFeature() {
    // ...
}
  • Проверка на конкретную версию: если нужно проверить точную версию (например, iOS 14.5), используйте #available(iOS 14.5, *).
  • Для macOS: пишите #available(macOS 10.15, *).

Итог: всегда используйте #available для проверки версии ОС в Swift. Это современно, надёжно и упрощает поддержку кода. Ваш код станет чище, а вероятность ошибок снизится.

<<>>

Проверка версии через UIDevice (для случаев, когда #available не подходит)

Иногда нужно получить именно строковое представление версии (например, для логирования или аналитики). В таких случаях можно использовать UIDevice.current.systemVersion, но преобразовывать его в сравнимый формат:

let version = UIDevice.current.systemVersion
let versionComponents = version.split(separator: ".").compactMap { Int($0) }
if versionComponents.count >= 2 {
    let major = versionComponents[0]
    let minor = versionComponents[1]
    if major >= 13 && minor >= 0 {
        print("iOS 13+")
    }
}

Этот подход полезен, когда вы не знаете точные константы для #available (например, при работе с динамическими данными). Но помните: для обычных проверок используйте #available — он короче и понятнее.

Ещё один вариант — использовать OperatingSystemVersion с кастомным сравнением:

let target = OperatingSystemVersion(majorVersion: 13, minorVersion: 0, patchVersion: 0)
let current = ProcessInfo.processInfo.operatingSystemVersion
if current >= target {
    // iOS 13+
}

Для этого нужно реализовать оператор сравнения (или использовать готовый из Swift 5.7+). Но это уже излишне — #available покрывает 99% случаев.