Добавить объявление

OptionSet: эффективная работа с флагами и настройками в Swift

Привет! Сегодня хочу разобрать один из самых недооцененных инструментов в Swift: OptionSet. Это не просто протокол, а элегантное решение для задач, с которыми мы сталкиваемся почти в каждом проекте.

Для чего нужен OptionSet:


Представьте, что вы создаете компонент списка задач. В разных местах приложения вам нужно показывать разные возможности: где-то поиск и фильтр, где-то только сортировку, а в третьем месте вообще все элементы управления сразу.

Чаще всего, когда в коде нужно описать какие-то варианты выбора, разработчики сразу вспоминают про enum. Это действительно правильный подход для ситуаций, где может быть выбран только один вариан, например при статусах заказа: «в обработке», «доставляется» или «выполнен».

Но вот в чем проблема: когда нам нужно выбрать сразу несколько вариантов одновременно (например, показать и поиск, и фильтр, и сортировку), обычное перечисление не подходит. Пришлось бы создавать отдельные варианты для всех возможных комбинаций: «поиск_и_фильтр», «поиск_и_сортировка» и так далее. Это очень неудобно и громоздко.

OptionSet решает эту задачу гораздо элегантнее. Он работает как набор независимых переключателей, представьте себе чекбоксы на сайте. Вы можете включать и выключать их в любой комбинации и для этого не нужно заранее прописывать все возможные сочетания.

Пример:


Допустим, мы делаем тот самый список задач. Вот как может выглядеть наша конфигурация:

struct TasksListOptions: OptionSet {
let rawValue: Int

static let showFilter = TasksListOptions(rawValue: 1 << 0) // 0001
static let showSearch = TasksListOptions(rawValue: 1 << 1) // 0010
static let showSort = TasksListOptions(rawValue: 1 << 2) // 0100
static let showLayoutSelector = TasksListOptions(rawValue: 1 << 3) // 1000
}

Вся мощь OptionSet раскрывается, когда мы начинаем его использовать. Допустим, у нас есть экран «Сегодня» и экран «Все задачи». Для каждого из них мы можем легко задать нужный набор опций прямо при создании:

let todayOptions: TasksListOptions = [.showFilter, .showSearch]

let allTasksOptions: TasksListOptions = [.showFilter, .showSearch, .showSort, .showLayoutSelector]

// Для архива ничего не нужно
let archiveOptions: TasksListOptions = []

Теперь в коде это выглядит чисто и понятно. Когда мы создаем ViewModel для каждого экрана, мы просто передаем соответствующий набор опций:

let todayViewModel = TasksListViewModel(options: todayOptions)
let allTasksViewModel = TasksListViewModel(options: allTasksOptions)
let archiveViewModel = TasksListViewModel(options: archiveOptions)

Самое удобное, что мы можем легко проверять, какие опции активны в любой момент:

class TasksListViewModel {
let options: TasksListOptions

var shouldShowSearch: Bool {
return options.contains(.showSearch)
}

var shouldShowSort: Bool {
return options.contains(.showSort)
}
}

Технически, можно было бы использовать Set<Enum>, но зачем?

Приемущества OptionSet:


  • Производительность: работа на уровне битовых операций - это максимально быстро.

  • Минимальное потребление памяти: все флаги хранятся в одном числе.

  • Готовый API: contains, insert, remove, union и другие операции из коробки.

Вывод:


OptionSet - это один из тех инструментов, который кажется сложным только до первого использования. На практике он очень упрощает код, делает его безопаснее и выразительнее.
05.11.2025 8 491