Перечисления с ассоциированными значениями
Перечисление описывает значение, которое может быть одним из заранее известных вариантов. В Swift enum заметно мощнее, чем в большинстве языков.
Суть урока: enum в Swift умеет нести данные внутри каждого случая (ассоциированные значения). Это превращает его в идеальный инструмент для моделирования состояний экрана: загрузка, успех, ошибка.
Базовое перечисление — список вариантов:
enum Direction {
case north, south, east, west
}
let move = Direction.north
switch move {
case .north: print("Вверх")
case .south: print("Вниз")
default: print("В сторону")
}Случаям можно задать сырое значение (raw value) одного типа — удобно для кодов и идентификаторов:
enum Planet: Int {
case mercury = 1, venus, earth, mars
}
print(Planet.earth.rawValue) // 3
let p = Planet(rawValue: 4) // Optional(.mars)Но настоящая суперсила — ассоциированные значения: каждый случай может нести свои данные. Это позволяет точно описать состояние:
enum LoadingState {
case idle
case loading
case success(data: [String])
case failure(message: String)
}
let state = LoadingState.success(data: ["A", "B"])
switch state {
case .idle: print("Ожидание")
case .loading: print("Загрузка...")
case .success(let d): print("Готово: \(d.count) элементов")
case .failure(let m): print("Ошибка: \(m)")
}Такой enum делает невозможные состояния невыразимыми: нельзя одновременно «грузиться» и «иметь ошибку». Компилятор гарантирует, что вы обработаете каждый случай.
Попробуй сам ▶ — запусти код прямо в браузере (Pyodide). Здесь нет Swift, но логика та же, что под капотом мобильного кода:
# Имитируем enum с ассоциированными значениями через кортеж (тег, данные).
def render(state):
tag = state[0]
if tag == 'idle':
return 'Ожидание'
elif tag == 'loading':
return 'Загрузка...'
elif tag == 'success':
return f'Готово: {len(state[1])} элементов'
elif tag == 'failure':
return f'Ошибка: {state[1]}'
print(render(('idle',)))
print(render(('success', ['A', 'B'])))
print(render(('failure', 'нет сети')))Как работает под капотом
Под капотом enum хранит компактный тег (какой случай выбран) и, при наличии, ассоциированные данные. Optional, который мы изучали, — это и есть enum с двумя случаями: .none и .some(Wrapped). Исчерпывающий switch заставляет обработать каждый вариант, а при добавлении нового случая компилятор подсветит все места, которые надо обновить — мощная страховка при рефакторинге.
Частые ошибки
- Путать сырые и ассоциированные значения. Raw value — одинаковый тип для всех случаев; ассоциированные данные — свои у каждого случая.
- Забыть let при извлечении данных.
case .success(let d)— let связывает данные с переменной. - Добавлять default «на всякий случай». Это лишает вас подсказок компилятора при добавлении нового случая.
Best practices
- Моделируйте состояния экрана через enum — это исключает невозможные комбинации.
- Избегайте default для своих enum, чтобы компилятор напоминал о новых случаях.
- Сырые значения удобны для сопоставления с кодами API и сериализации.
Итоги. Перечисления Swift с ассоциированными значениями — выразительный способ моделировать ограниченный набор состояний с данными. Они делают невозможные состояния непредставимыми и в паре с switch дают строгую, безопасную логику.
Шире контекста
Перечисления с ассоциированными значениями — это инструмент, который меняет сам подход к проектированию. Вместо того чтобы держать несколько отдельных булевых флагов и опционалов (isLoading, errorMessage, data) и молиться, чтобы они не разошлись, вы описываете одно перечисление, где каждое состояние самодостаточно и несёт ровно те данные, что ему нужны. Так невозможные комбинации (одновременно «грузится» и «ошибка») становятся буквально невыразимыми в типе — компилятор не даст их создать. Этот приём называют «делать недопустимые состояния непредставимыми», и он лежит в основе надёжной архитектуры. В сочетании с исчерпывающим switch вы получаете двойную страховку: и данные структурированы корректно, и каждый случай гарантированно обработан. Перечисления Swift по выразительности приближаются к алгебраическим типам данных из функциональных языков, оставаясь при этом понятными и практичными для повседневной мобильной разработки.