Avatar ·

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

Всем привет! Я только начал изучать Swift и столкнулся с проблемой при работе с перечислениями (enum). У меня есть enum, который содержит строковые значения для категорий товаров: ```swift enum Category: String { case electronics = "Электроника" case clothing = "Одежда" case books = "Книги" case sports = "Спорт" } ``` Мне нужно вывести все доступные категории в виде списка для picker'а, но я не могу понять, как перебрать все case'ы этого enum. Я пробовал использовать `Category.allCases`, но компилятор ругается, что такого свойства нет. Пытался сделать через `for category in Category`, но тоже не работает. В документации написано про протокол `CaseIterable`, но я не до конца понимаю, как его правильно применить. Когда я добавляю `CaseIterable`, код компилируется, но я не уверен, что делаю это правильно. Подскажите, пожалуйста, как правильно организовать перебор всех значений enum с типом String? И можно ли получить не только сами case'ы, но и их rawValue (строковые значения) при переборе? Буду очень благодарен за примеры кода! <<>>

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

В Swift для перебора всех значений enum существует специальный протокол — CaseIterable. Он автоматически генерирует свойство allCases, которое возвращает массив всех case'ов перечисления в порядке их объявления.

Вот как это работает с вашим примером:

enum Category: String, CaseIterable {
    case electronics = "Электроника"
    case clothing = "Одежда"
    case books = "Книги"
    case sports = "Спорт"
}

// Теперь можно перебрать все значения
for category in Category.allCases {
    print("Case: \(category), rawValue: \(category.rawValue)")
}

// Вывод:
// Case: electronics, rawValue: Электроника
// Case: clothing, rawValue: Одежда
// Case: books, rawValue: Книги
// Case: sports, rawValue: Спорт

Что важно знать:

  • CaseIterable — это протокол, который нужно явно указать при объявлении enum
  • allCases — автоматически сгенерированное свойство, возвращающее массив всех case'ов
  • Порядок элементов в allCases соответствует порядку объявления case'ов в коде
  • Протокол работает с любым типом rawValue (String, Int, Float и т.д.)

Практический пример для picker'а:

import UIKit

enum Category: String, CaseIterable {
    case electronics = "Электроника"
    case clothing = "Одежда"
    case books = "Книги"
    case sports = "Спорт"
}

class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
    
    let pickerView = UIPickerView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        pickerView.dataSource = self
        pickerView.delegate = self
    }
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return Category.allCases.count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return Category.allCases[row].rawValue
    }
    
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        let selectedCategory = Category.allCases[row]
        print("Выбрана категория: \(selectedCategory.rawValue)")
    }
}

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

Вы можете получить не только rawValue, но и сам case для дальнейшей работы:

// Фильтрация категорий
let electronicCategories = Category.allCases.filter { $0 == .electronics }

// Поиск по rawValue
if let category = Category(rawValue: "Одежда") {
    print("Найдена категория: \(category)")
}

// Создание массива строк для UI
let categoryNames = Category.allCases.map { $0.rawValue }
print(categoryNames) // ["Электроника", "Одежда", "Книги", "Спорт"]

Важно помнить: если ваш enum имеет связанные значения (associated values), то CaseIterable не сработает автоматически — вам придётся реализовать allCases вручную. Но для простых перечислений с rawValue это идеальное решение.

<<>>

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

Если по каким-то причинам вы не хотите использовать CaseIterable (например, для версий Swift ниже 4.2 или для enum с associated values), можно реализовать свойство allCases вручную:

enum Category: String {
    case electronics = "Электроника"
    case clothing = "Одежда"
    case books = "Книги"
    case sports = "Спорт"
    
    static var allCases: [Category] {
        return [.electronics, .clothing, .books, .sports]
    }
}

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

Когда это может пригодиться:

  • Вы поддерживаете старую версию Swift (до 4.2)
  • Нужно исключить некоторые case'ы из перебора
  • Enum содержит associated values, и автоматическая генерация невозможна
  • Хотите изменить порядок элементов в переборе

Недостаток этого подхода — при добавлении нового case в enum нужно не забыть обновить свойство allCases вручную. В остальном он работает так же, как и автоматическая версия.