Атрибут @backDeployed в Swift позволяет расширить доступность функций для старых версий ОС. Разработчикам фреймворков выгодно сделать новые декларации доступными для приложений с более низкой минимальной целью развертывания.
В SE 376 Function Back Deployment атрибут был представлен в качестве предложения, после чего он был впервые реализован в Swift 5.8. Многие разработчики ожидали, что эта новая возможность позволит Apple осуществлять обратное развертывание многих функций SwiftUI, однако она оказалась недостаточно гибкой. Давайте погрузимся в эту тему!
Как работает атрибут @backDeployed?
Атрибут @backDeployed позволяет выполнять обратное развертывание функций и делать их доступными для приложений, работающих на старых версиях ОС.
Например, представьте, что у нас есть новый тип Temperature, представленный несколько лет назад в iOS 15:
@available(iOS 15, *) public struct Temperature { public var degreesCelsius: Double // ... }
Спустя несколько лет компания Apple решила реализовать в своем SDK для iOS 17 часто запрашиваемую функцию:
extension Temperature { @available(iOS 17, *) public var degreesFahrenheit: Double { return (degreesCelsius * 9 / 5) + 32 } }
Атрибут availability отмечает, что метод доступен только для iOS 17 и выше. Однако метод является самодостаточным и может работать без изменений в более ранних версиях ОС, поскольку нам нужен только входной параметр degreesCelsius. Поэтому можно пометить метод как “обратно развернутый/совместимый”:
extension Temperature { @available(iOS 15, *) @backDeployed(before: iOS 17) public var degreesFahrenheit: Double { return (degreesCelsius * 9 / 5) + 32 } }
Мы также обновили атрибут доступности, поскольку метод стал доступен в качестве back-deployed функции для старых версий ОС.
Поведение обратно развернутых API
Чтобы лучше понять, как выполняются back-deployed функции, полезно посмотреть на конечный код, сгенерированный компилятором:
print(temperature.degreesFahrenheit_bridge) extension Temperature { var degreesFahrenheit_bridge: Double { if #available(iOS 17.0, *) { /// Call the original parameter since it's available on this OS version. return degreesFahrenheit } else { /// Use the local copy instead. return degreesFahrenheit_fallback } } }
Компилятор создает мост между исходным свойством и обратно развернутой функцией. Новое свойство заменяет нашу оригинальную ссылку внутри оператора print, чтобы обеспечить корректную поддержку старых версий ОС.
Свойство-мост проверяет доступность API и принимает решение о запуске оригинальной реализации или копии функции, которая выдается клиенту.
Кому следует использовать обратное развертывание функций?
Обратное развертывание функций полезно в первую очередь для разработчиков SDK. При обычной разработке приложения мы поставляем последнюю версию библиотеки непосредственно с приложением, исключая необходимость использования атрибута @backDeployed. Другими словами, весь новый код, который мы пишем, будет доступен сразу же и не требует обратного развертывания.
Почему Apple не может выполнить обратное развертывание всех новых API для старых версий ОС?
Apple будет использовать атрибут @backDeployed, когда это возможно, чтобы сделать методы доступными для приложений, поддерживающих старые версии ОС. Вы можете воспользоваться поиском на GitHub, чтобы найти примеры в открытом репозитории Swift:
Обратное развертывание работает только с функциями, методами и субскриптами. Вероятно, Apple не выполняет обратное развертывание новых API из-за отсутствия поддержки более широких функциональных возможностей новых API.
Заключение
Обратное развертывание функций с использованием атрибута @backDeployed — отличный инструмент для разработчиков SDK, позволяющий сделать свои API доступными для клиентов, работающих на старых версиях ОС. Хотя это не решение для всех новых API, оно уже позволяет работать с некоторыми новыми самодостаточными Swift API.