Источник: Nuances of Programming
Наряду со многими новыми функциями, которые появились в Swift 5.1, одна из самых интересных — это врапперы свойств. По сути врапперы находятся между поведением свойств и их хранением. Врапперы свойств определяются с помощью struct, class, or enum. Также они могут применяться, если мы задаем свойства внутри этих типов.
Swift уже предоставлял несколько встроенных врапперов в предыдущих версиях, например lazy, @NSCopying, но с врапперами свойств разработчик может внедрять собственные без усложнения языка. О том, как это работает, можно прочесть в документации по ссылке.
Врапперы свойств активно используются в SwiftUI. Фреймворк предоставляет множество врапперов, например:
- @State. Его значение привязывается к представлению, в котором оно объявлено.
- @Binding. Предается вниз от родительского свойства State с использованием$ projectedValue.
- @ObservedObject. Похож на@State, но используется для свойства, которое соотносится с протоколом ObservableObject. ObservableObject должен быть типом class и обновлять представление, когда изменяются свойства, помеченные @Published.
- @Published. Этот враппер используется для свойств, заявленных в ObservableObject. Всякий раз, когда значение меняется, враппер вызывает метод objectWillChange, чтобы представление реагировало на изменения.
- @EnvironmentObject. Похож на @ObservedObject, но может использоваться для обмена данными между различными представлениями сверху вниз по иерархии без передачи явного свойства дочернему представлению.
- @Environment. Используется для внедрения и коррекции общесистемной конфигурации — цветовой схемы системы, направления макета, размера содержимого — внутри представления.
Врапперы свойств не ограничены только SwiftUI. Со Swift 5.1 можно создавать пользовательские врапперы свойств! Вот, что можно делать , используя пользовательские врапперы:
- Преобразовывать значение после того, как оно уже было назначено.
- Задавать минимальные и максимальные границы значения.
- Передавать свойству дополнительное значение.
- Создавать враппер, ведущий себя как делегат, скрывающий детали реализации API.
Это всего лишь несколько примеров врапперов, которые можно создавать; возможности по сути безграничны! Давайте создадим несколько врапперов и посмотрим, как с их помощью можно упростить код.
Использование врапперов свойств в двух словах
Создать новый враппер очень просто:
- Объявите ключевое слово @propertyWrapper до того, как объявить тип, в котором хотите использовать враппер свойства. Это может быть struct, class или enum.
- Мы должны реализовать свойство wrappedValue. Обычно в этом свойстве объявляются пользовательские setter и getter. Это свойство может быть computed или stored.
- При присвоении свойству значения при объявлении, блок инициализации передаст wrappedValue . Также можно создать пользовательский блок инициализации с дополнительными свойствами. Ниже в примерах с враппером @Ranged мы рассмотрим это подробнее.
- Можно задать дополнительное свойство projectedValue любого типа, с помощью префикса $ из свойства.
Чтобы использовать его, мы добавляем префиксом @ к врапперу, когда объявляем свойство в типе.
Теперь давайте внедрим пользовательские врапперы!
Преобразование значения свойства
Для этого @Uppercased враппера, мы хотим убедиться, что String всегда выводится в верхнем регистре, когда внутри свойства задано значение. Вот что нужно сделать для реализации:
- Сохраняем нужную строку внутри свойства, названного text.
- Необходимое wrappedValue свойство — вычисляемое. Всякий раз, когда мы задаем значение, оно будет храниться в свойстве text. Каждый раз при получении свойства, значение будет возвращено в верхнем регистре.
- Создаем блок инициализации wrappedValue и назначаем его свойству text при первой инициализации враппера.
- Чтобы запустить, просто добавляем ключевое слово @Uppercased перед свойством.
Обозначение минимальной и максимальной границ числового значения
@Ranged враппер можно использовать для фиксации значения числа, задав минимальную и максимальную границы значения. Каждый раз, когда присваивается значение, производится сравнение, и значение присваивается на основании следующих условий:
- Если новое присваиваемое значение больше максимальной границы, в свойстве сохраняется максимальное значение.
- Если новое значение меньше минимальной границы, в свойстве сохраняется минимальное значение.
- Если условия не пересекаются, в свойстве сохраняется новое значение.
Чтобы принять минимальный и максимальный параметры, создается пользовательский блок инициализации. Когда мы объявляем свойство, нам также нужно передать значения максимума и минимума после объявления @Ranged.
Свойство Project Date для отформатированной в ISO8601 строки
Врапперы свойств можно использовать, чтобы передавать другое значение любого типа, используя свойство projectedValue с префиксом $. Для ISO8601DateFormatter используется статичный private ISO8601DateFormatter. Каждый раз при чтении projectedValue преобразовывает дату из сохраненного свойства wrappedValue.
Враппер свойства NSLocalizedString API
Враппер свойства @Localizable используется для оборачивания NSLocalizedString API. Когда свойство объявлено с использованием ключевого слова @Localizable, значение будет сохранено в приватном свойстве key и будет использовано каждый раз, когда wrappedValue доступно при передаче NSLocalizedString(key:comment:) блоку инициализации для получения локализованной строки из приложения.
Оборачивание UserDefaults API
UserDefaults API может быть очень громоздким при сохранении и извлечении значений по умолчанию. Его можно упростить созданием враппера, который спрячет выполнение вызовов API.
Враппер @UserDefault принимает 2 параметра в блоке инициализации, key и initialValue в том случае, если значение ключа не доступно в UserDefaults . Сам wrappedValue — это враппер-вычислитель. Он использует сохраненный key каждый раз, когда задано значение. При чтении свойства, key используется для получения значения generic. Если значение не доступно, вместо него вернется initialValue.
Заключение
Врапперы свойств — великолепная функция, с помощью которой можно предоставлять пользовательские шаблоны и поведение настройкам, заданным в типе для упрощения кода. Я очень надеюсь, что в будущем сообщество будет создавать и распространять еще больше врапперов.
Читайте также:
Читайте нас в телеграмме и vk
Перевод статьи Alfian Losari: Understanding Property Wrappers in Swift By Examples