Avatar ·

Как перебрать все значения enum с типом String в Swift?

Всем привет! Я новичок в Swift и столкнулся с проблемой при работе с перечислениями. У меня есть enum, который содержит названия дней недели со строковыми значениями: ```swift enum WeekDay: String { case monday = "Понедельник" case tuesday = "Вторник" case wednesday = "Среда" case thursday = "Четверг" case friday = "Пятница" case saturday = "Суббота" case sunday = "Воскресенье" } ``` Мне нужно вывести все дни недели в консоль или отобразить их в TableView. Но я не могу понять, как получить массив всех кейсов этого enum. Я пробовал использовать `WeekDay.allCases`, но Xcode говорит, что `WeekDay` не соответствует протоколу `CaseIterable`. Когда я пытаюсь добавить `CaseIterable`, компилятор ругается. Также я видел в интернете примеры со старыми версиями Swift, где использовались вспомогательные функции. Но у меня Xcode 15 и Swift 5.9 — возможно, есть более современный способ? Мне нужно решение, которое работает с типом String. Буду благодарен за подробное объяснение с примерами кода. Спасибо! <<>>

Решение: используйте протокол CaseIterable

В Swift 5.9 и Xcode 15 ваша проблема решается очень просто — нужно добавить протокол CaseIterable к вашему перечислению. Давайте разберем правильный синтаксис:

enum WeekDay: String, CaseIterable {
    case monday = "Понедельник"
    case tuesday = "Вторник"
    case wednesday = "Среда"
    case thursday = "Четверг"
    case friday = "Пятница"
    case saturday = "Суббота"
    case sunday = "Воскресенье"
}

// Теперь можно перебрать все значения:
for day in WeekDay.allCases {
    print(day.rawValue) // Выведет: Понедельник, Вторник...
}

После добавления CaseIterable у перечисления автоматически появляется статическое свойство allCases, которое возвращает массив всех кейсов в порядке их объявления.

Почему это работает?

  • CaseIterable — это протокол из стандартной библиотеки Swift, который автоматически генерирует коллекцию всех кейсов для перечислений без associated values.
  • Ваше перечисление имеет raw values (String), но это не мешает протоколу работать — он просто собирает сами кейсы, а их значения можно получить через свойство rawValue.
  • Протокол доступен начиная с Swift 4.2 (Xcode 10+).

Практический пример: отображение в TableView

class DaysViewController: UITableViewController {
    
    let days = WeekDay.allCases
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return days.count // 7
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "DayCell", for: indexPath)
        let day = days[indexPath.row]
        cell.textLabel?.text = day.rawValue // "Понедельник" и т.д.
        return cell
    }
}

Дополнительные возможности

Вы также можете получить индекс кейса, если нужно:

if let index = WeekDay.allCases.firstIndex(of: .friday) {
    print("Пятница под индексом \(index)") // 4
}

Или создать словарь для быстрого доступа:

let dayDict = Dictionary(uniqueKeysWithValues: WeekDay.allCases.map { ($0.rawValue, $0) })
print(dayDict["Среда"] == .wednesday) // true

Важно: Если вы используете Xcode 10 и выше, этот подход работает без каких-либо дополнительных настроек. В более старых версиях Swift придется использовать ручную реализацию (см. альтернативный ответ).

<<>>

Альтернатива: ручная реализация для старых версий Swift

Если по какой-то причине вы не можете использовать CaseIterable (например, проект на Swift 4.1 или ниже), можно добавить статический массив вручную:

enum WeekDay: String {
    case monday = "Понедельник"
    case tuesday = "Вторник"
    case wednesday = "Среда"
    case thursday = "Четверг"
    case friday = "Пятница"
    case saturday = "Суббота"
    case sunday = "Воскресенье"
    
    static var allCases: [WeekDay] {
        return [.monday, .tuesday, .wednesday, .thursday, .friday, .saturday, .sunday]
    }
}

// Использование:
for day in WeekDay.allCases {
    print(day.rawValue)
}

Минус такого подхода — если вы добавите новый кейс, нужно не забыть обновить массив вручную. Поэтому для современных проектов всегда предпочтительнее использовать CaseIterable.

Также можно использовать рефлексию через Mirror, но это излишне усложнит код и снизит производительность — такой вариант не рекомендуется.