Сохранение логики фильтрации в Swift Combine. Часть 2

Автор оригинала: Kevin Cheng
  • Перевод
Дата-ориентированный Combine





Перевод статьи подготовлен специально для студентов продвинутого курса «iOS Разработчик».





В предыдущем эпизоде мы успешно смоделировали поток значений, где к каждому значению был привязан один простой оператор (delay).

В этой части мы рассмотрим еще несколько операторов, сделаем их Codeable и, наконец, преобразуем их в паблишер Combine во время выполнения.



Типы операторов


Прежде чем мы начнем их моделировать, нам нужно понять, какие существуют типы операторов.

Сайт ReactiveX разбивает их примерно на 10 категорий: создание, трансформация, фильтрация, объединение, обработка ошибок, вспомогательные, условные, математические/агрегатные, backpressure, connectable-observable и операторы для преобразования observable (To). Если вам интересно, у ReactiveX есть хорошее объяснение для каждого типа и оператора.
Примечание: Если вы не знакомы с RxSwift, Observable в RxSwift эквивалентен Publisher в Combine.
В предыдущей части мы упомянули оператор delay, который относится к вспомогательному (utility) типу. Сегодня мы сосредоточимся на сохранении двух фильтрационных (filtering) операторов.



Операторы фильтрации


Этот тип оператора препятствует всем или некоторым (или никаким) из элементов потока продвигаться дальше по потоку на основе данного условия.

dropFirst


dropFirst останавливает передачу первых n элементов. Мы можем просто добавить его в наше перечисление (enum) Operator, учитывая его простоту.

enum Operator {
 case delay(seconds: Double)
 case dropFirst(count: Int)
}

Мы также можем легко преобразовать этот кейс перечисления в Publisher.

extension Operator {func applyPublisher<T>(_ publisher: AnyPublisher<T, Never>) -> AnyPublisher<T, Never> { 
switch self {
   case .dropFirst(let count):
       return publisher.dropFirst(count).eraseToAnyPublisher()
 // пропустим остальные кейсы
 }}}

Теперь оператор dropFirst можно сохранить и отобразить в списке операторов.



Сохранение dropFirst схоже с оператором delay. Возможно, фильтрация не так уж сильно отличается от вспомогательных операторов. Что ж, давайте попробуем еще один оператор, прежде чем мы сделаем такой вывод.

Filter


В отличие от dropFirst, который имеет очень простые критерии фильтрации, оператор filter использует замыкание (closure) вместо примитивного типа. Это уже случай посложнее. Как мы сохраняем и распространяем замыкание?

filter(_:)

Повторно публикует все элементы, которые соответствуют заданному замыканию.

developer.apple.com


Давайте рассмотрим метод filter поближе.

func filter(_ isIncluded: @escaping (Self.Output) -> Bool) -> Publishers.Filter<Self>

Его замыкание isIncluded принимает универсальный тип и возвращает логическое значение.

Есть ли в Foundation что-то, что представляет логические условия и возвращает логическое значение? Напоминает что-нибудь?

Filter с NSPredicate


Ответ — NSPredicate. Если мы можем сохранить условия фильтрации в виде выражений в строковом формате, мы можем просто передать значение потока и использовать NSPredicate для оценки результатов.

Давайте продолжим и добавим filter в перечисление.

enum Operator {
 case delay(seconds: Double)
 case dropFirst(count: Int)
 case filter(expression: String)
}

Все, что нам нужно, это фильтровать выражения типа %d !=3 или %@ != “D”; следовательно, expression является соответствующим типом. Точно так же мы должны быть в состоянии переместить перечисление filter в Publisher.

extension Operator {
func applyPublisher<T>(_ publisher: AnyPublisher<T, Never>) -> AnyPublisher<T, Never> { 
 switch self {
   case .filter(let expression):
   return publisher.filter { value in
               NSPredicate(format: expression,
                           argumentArray: [value])
                .evaluate(with: nil) }.eraseToAnyPublisher()
      
     // пропустим остальные кейсы
 }}}

Как и планировалось, мы отправляем выражение в NSPredicate вместе со значением, отправляемым из Publisher.

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



Как вы можете наблюдать, Filter Stream добавляется в этот сохраненный массив операторов и преобразуется в Publisher, чтобы отфильтровать число 3 из вышестоящих значений.



В следующем эпизоде: Сохраняем операторы трансформации, Map и Scan


В GIF-демо вы можете обнаружить, что список операторов довольно пуст. В ближайшие несколько недель мы собираемся заполнить его различными типами операторов: операторами трансформации, map и scan.
Вы можете найти исходный код в этом combine-magic-swifui репозитории в папке combine-playground.

Ждем ваши комментарии и приглашаем на день открытых дверей по курсу.
OTUS. Онлайн-образование
Цифровые навыки от ведущих экспертов

Похожие публикации

Комментарии 0

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое