Ответ
Property Wrapper — это механизм в Swift, который позволяет инкапсулировать стандартную логику работы с getter и setter свойства в переиспользуемый тип. Он «оборачивает» значение свойства, добавляя к нему дополнительное поведение при чтении и записи.
Синтаксис и компоненты:
- Атрибут
@propertyWrapper: Помечает структуру или класс как обертку. - Свойство
wrappedValue: Обязательное свойство, которое содержит логику get/set для обернутого значения. - Инициализатор: Может принимать начальное значение.
- Свойство
projectedValue(опционально): Предоставляет дополнительный API для обернутого свойства (доступ через$).
Пример: Обертка для автоматического триммирования строки.
@propertyWrapper
struct Trimmed {
private var value: String = ""
var wrappedValue: String {
get { value }
set { value = newValue.trimmingCharacters(in: .whitespacesAndNewlines) }
}
init(wrappedValue: String) {
self.wrappedValue = wrappedValue // Используется setter
}
}
// Использование
struct UserProfile {
@Trimmed var username: String
@Trimmed var email: String
}
var profile = UserProfile(username: " john_doe ", email: " email@example.com ")
print(profile.username) // "john_doe" (пробелы удалены)
print(profile.email) // "email@example.com" (пробел удален)
Пример с projectedValue (логирование изменений):
@propertyWrapper
struct Logged<Value> {
private var value: Value
private let name: String
var wrappedValue: Value {
get { value }
set {
print("Свойство '(name)' изменено: (value) -> (newValue)")
value = newValue
}
}
var projectedValue: Self { self } // Проецируем саму обертку
init(wrappedValue: Value, _ name: String = "") {
self.value = wrappedValue
self.name = name
}
}
struct Settings {
@Logged("volume") var volume: Double = 0.5
}
var settings = Settings()
settings.volume = 0.7 // В консоли: "Свойство 'volume' изменено: 0.5 -> 0.7"
let wrapper = settings.$volume // Доступ к самой обертке через projected value
Распространенные применения:
- Валидация и нормализация (как в
@Trimmed). - Синхронизация с UserDefaults (
@UserDefault). - Ограничение значений (
@Clamped(min: 0, max: 100)). - Ленивая инициализация (встроенный
@Lazy). - Thread-safe доступ к свойствам.
Итог: Property Wrapper — мощный инструмент для соблюдения принципа DRY, позволяющий выносить стандартную логику работы со свойствами в отдельные, тестируемые компоненты.