Сделайте ваши кодовые базы UIKit такими же чистыми, как SwiftUI

Самое примечательное в SwiftUI то, что он выглядит невероятно. Он берет относительно большой кусок кода и сжимает его во что-то красивое и простое, так что даже непрограммисты могут понять, что происходит.

Но знаете ли вы, что, начиная со Swift 5.4, вы можете воспроизвести этот опыт во всем, что захотите?

Вы можете создавать запросы URL, сложные структуры данных и даже целый экран в UIKit так же, как в SwiftUI.

Другими словами, вы можете очень легко внедрить DSL в свои приложения.

Подождите... DSL?

Если вы еще не слышали термин «DSL», самое время поговорить об этом.

DSL означает Доменный язык.

Но что это значит?

Когда мы смотрим на «обычный» язык программирования, например — Swift, мы видим, что теоретически мы можем построить все, что захотим.

Все зависит от структуры, которую мы имеем внизу.

Например, мы можем создавать серверные приложения, веб и Android, если только у нас будет надлежащий фреймворк.

Классы, структуры и перечисления не являются специфическими функциями iOS.

С другой стороны, DSL — это язык, написанный для решения конкретной области или проблемы.

Одним из хороших примеров является HTML.

Основная цель HTML — описать документ, отображаемый на веб-странице.

HTML отлично подходит для этой цели и может быть легко прочитан непрограммистами.

Другой пример — XML или JSON, которые отлично подходят для хранения общей структуры данных.

Возвращаясь к Swift — SwiftUI — это DSL. Его основная цель — описать динамический экран, и он также построен на основе Swift, который является GPL (язык общего назначения).

Чтобы создать больше кода в стиле DSL, нам нужно использовать что-то под названием Result Builder, которое является частью Swift Language и может помочь нам создать красивый код.

Конструктор результатов

Давайте попробуем понять, как работает Result Builder, на примере, и мы возьмем самый простой — структуру данных.

Предположим, мы хотим описать структуру данных, построенную из Team и Player.

Наша структура данных может выглядеть примерно так:

Это стандартный фрагмент кода структур.

Вы видите все скобки? Это запах кода. Мы можем использовать построитель результатов, чтобы сделать наш код более понятным.

Первое, что мы хотим сделать, это взять блок со списком игроков (аналогично тому, что мы видели в SwiftUI) и построить из него массив:

Это наш первый конструктор результатов!

Конструктор результатов — это просто структура, помеченная @resultBuilder сверху.

Основной метод, который нам нужно добавить для всех построителей результатов, — это buildBlock, который принимает объекты с переменным числом переменных и возвращает массив. Вот и все!

Чтобы создать новый построитель результатов, мы можем просто создать новую переменную с префиксом @TeamBuilder:

Круто, ха?

Мы убрали скобки и запятые, и наш код очень похож на SwiftUI и HTML!

Давайте добавим модификаторы

Следующий совет не относится к Result Builder, но если мы здесь имеем дело с простым кодом, то почему бы не рассказать об этом?

Мы можем улучшить читаемость нашего кода, используя модификаторы.

Если вы раньше писали код SwiftUI, вы, вероятно, знаете, что такое модификаторы.

Теперь модификатор — это не «функция», а скорее простой шаблон проектирования, который может помочь вашему коду стать намного более читабельным, когда у вас много свойств.

С помощью модификаторов вы можете объединять свойства независимо от их порядка и отделять их друг от друга.

Мы делаем это, дублируя структуру, устанавливаем новое значение и возвращаем обновленную копию:

Добавим условие if-then-else

Предоставление списка игроков немного скучно.

Мы хотим, чтобы наш DSL был намного более сложным. Для этого попробуем реализовать некоторую логику:

Выгляди просто, а?

Не так быстро. Попытка построить приведенный выше код приводит к ошибке (но, по крайней мере, построитель результатов «выдает» что-то):

"Замыкание, содержащее оператор потока управления, не может использоваться с построителем результатов "TeamBuilder""

Причина в том, что статическая функция buildBlock ожидает получить вариативный список объектов. Если-то-иначе не является частью его ожидания.

Чтобы добавить поддержку If-then-else, нам нужно реализовать еще три метода:

buildOptional и buildEither (в двух вариантах).

Одно важное правило, которое вы должны помнить о resultBuilder, заключается в том, что если вы хотите, чтобы он поддерживал дополнительные методы, а не buildBlock, компонент и результат должны быть одного типа. Что-то вроде того:

static func buildBlock(_ components: [Player]…) -> [Player] {

Но это заставит нас создать список массивов Player, а не только список Player.

Для поддержки как Player, так и [Player] мы можем использовать протокол:

PlayerGroup может быть либо одним Player, либо массивом Player.

Теперь давайте посмотрим, как добавить дополнительные методы:

Перекомпилируйте код, и он работает!

Обратите внимание, что я изменил метод buildBlock, чтобы он работал с PlayerGroup вместо Player.

Реализовать в инициализаторе

Когда у вас есть построитель результатов, вы можете реализовать его как часть метода инициализации:

Я добавил модификаторы в структуру Team, но это необязательно и только здесь, чтобы сделать код более похожим на SwiftUI.

Хватит заниматься спортом. Что еще?

С Result Builder вы можете делать удивительные вещи, ограниченные вашим воображением и навыками.

Вот лишь некоторые идеи:

- Рендеринг экранов в UIKit

- Создание HTTP-запросов.

- Рисуйте фигуры с помощью Core Graphics.

- Создание структур данных.

- Создание строк с атрибутами.

- Создание анимации по ключевым кадрам.

И все это на вашем собственном понятном и простом «языке».

Например, следующий код представляет собой встроенный построитель результатов Path:

Можете ли вы придумать дополнительные примеры?