Свойства-обёртки в Swift
property wrapper - перехват доступа к свойству
В проектах часто повторяется одна и та же логика вокруг свойства: чтение/запись в UserDefaults, валидация, потокобезопасный доступ, синхронизация с UI. Копировать её в каждом поле утомительно и ошибочно.
Свойство-обёртка (property wrapper) — тип, который перехватывает доступ к помеченному свойству: при чтении и записи вызывается код обёртки, а снаружи синтаксис остаётся обычным (settings.isDarkMode = true).
Обёртки появились в Swift 5.1; в экосистеме Apple они стали основой SwiftUI (@State, @Binding, …) и части Combine.
Минимальная своя обёртка
@propertyWrapper
struct Clamped {
private var value: Int
let range: ClosedRange<Int>
init(wrappedValue: Int, _ range: ClosedRange<Int>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: Int {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
}
struct Player {
@Clamped(0...100) var health = 100
}
Обязательные элементы типа-обёртки:
| Член | Назначение |
|---|---|
wrappedValue | То, что видит код при обращении к свойству |
init(wrappedValue:) или кастомный init | Начальное значение и параметры обёртки |
@propertyWrapper на типе | Помечает тип как обёртку |
Компилятор разворачивает @Clamped в хранение экземпляра обёртки и прокси-доступ к wrappedValue.
Параметры обёртки
Аргументы в скобках после @ передаются в инициализатор:
@Clamped(0...100) var stamina = 50
Можно задавать несколько параметров и значения по умолчанию — как у обычного инициализатора структуры.
projectedValue и $
Некоторые обёртки предоставляют проецируемое значение — доступ через префикс $ к дополнительному API (часто Binding в SwiftUI):
struct ContentView: View {
@State private var count = 0
var body: some View {
Stepper("Счёт: \(count)", value: $count)
}
}
count — обычное чтение/запись; $count — проекция для двусторонней связи с UI. Это не «магия доллара», а соглашение: если у обёртки есть projectedValue, компилятор генерирует $имя.
Подробнее про UI — фреймворки и жизненный цикл.
Пример без UI — настройки приложения
Повторяющийся доступ к UserDefaults удобно вынести в обёртку (упрощённый учебный вариант):
@propertyWrapper
struct Storage<T> {
let key: String
let defaultValue: T
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
struct AppSettings {
@Storage(key: "darkMode", defaultValue: false)
static var isDarkMode: Bool
}
В продакшене для сложных типов добавляют кодирование (Codable) и потокобезопасность; идея та же — одна реализация, много свойств.
Более развёрнутый пример — в рекомендациях по разработке.
Где встречаются в стандартной экосистеме
| Обёртка | Контекст |
|---|---|
@State, @Binding, @ObservedObject | SwiftUI — состояние и связь с UI |
@Published | Combine — уведомление об изменении |
@MainActor | Изоляция на главном акторе (async) |
@UIApplicationDelegateAdaptor и др. | Мосты в SwiftUI App |
Перед SwiftUI достаточно понять модель: обёртка = тип + доступ к wrappedValue. Тогда аннотации Apple читаются как конкретные реализации той же идеи.
Ограничения и правила
- Обёртку можно применять к stored property в
struct,class,enum; дляstaticиclass— отдельные правила версии Swift. - Нельзя обернуть вычисляемое свойство без backing storage так же просто, как хранимое — компилятор требует место для экземпляра обёртки.
- Обёртки на уровне локальных переменных и параметров поддерживаются в новых версиях языка — проверяйте целевую версию в
Package.swift/ настройках проекта. - Тестируйте обёртку отдельно: она должна вести себя предсказуемо при get/set.
Обёртки и расширения и макросы
| Подход | Когда |
|---|---|
| Property wrapper | Повторяющаяся логика доступа к одному свойству |
| extension | Новые методы/вычисляемые свойства для типа |
| Макросы (Swift 5.9+) | Генерация кода на этапе компиляции, boilerplate типов |
Исторический контекст эволюции — история Swift.
Практические советы
- Не оборачивайте всё подряд — обёртка оправдана при трёх и более повторениях одного паттерна.
- Документируйте, что делает get/set (потоки, персистентность, побочные эффекты).
- Имя обёртки должно читаться в объявлении:
@ValidatedEmail var emailпонятнее@Wrapper1. - Для SwiftUI сначала освойте
@Stateи@Binding, затем@ObservedObject/@EnvironmentObject.
Связанные материалы
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). История Swift - эволюция языка от замены Objective-C до современного инструмента разработки в экосистеме Apple. Экосистема приложений на Swift - инструменты, фреймворки и сценарии разработки в среде Apple. Swift — это современный, безопасный и производительный язык программирования общего назначения, разработанный компанией Apple. Набор советов, правил, принципов и обычаев в разработке на этом языке. Интерфейс класса служит контрактом между разработчиком класса и его пользователями. Он определяет, как взаимодействовать с объектом, не раскрывая механизмов работы. Простые приложения на Swift — CLI, Codable, файлы и сеть на Foundation. Основы Swift - базовый синтаксис, стандартные фреймворки и старт разработки в экосистеме Apple. Строки и Character, интерполяция, точка и вызов методов, запятая, опциональная точка с запятой, скобки и соглашения Swift для iOS и macOS. Ключевые слова Swift - справочник по основным конструкциям языка и их практическому применению. Набор функций, которые включены в стандартную библиотеку языка. Типизация, набор правил определения типа данных значений языка. Управляющие конструкции и циклы в Swift - условия, итерации и безопасное управление потоком выполнения.История языка Swift
Экосистема приложений на Swift
Что требуется знать перед началом изучения языка программирования Swift
Рекомендации по разработке на Swift
Объектно-ориентированное программирование в Swift
Простые приложения на Swift
Основы языка Swift
Синтаксис и пунктуация в Swift
Ключевые слова языка Swift
Встроенные функции и методы Swift
Типы данных и объявление переменных
Управляющие конструкции и циклы в Swift