Введение в RxSwift. Часть 4. Операторы


Логотип проекта ReactiveX

Операторы позволяют удобно преобразовывать последовательность приходящих событий в RxSwift. Их можно разделить на несколько основных групп (назначение каждой из групп, думаю, говорит само за себя):

  • Фильтрующие
  • Трансформирующие
  • Комбинирующие

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

Для большинства примеров я буду использовать объект типа Observable.

let elements = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let observable = Observable.from(elements)

Для демонстрации примеров в Playgrounds я реализовал вспомогательный метод, который создаёт подписку и выводит полученные значения в консоль.

func example<T>(of name: String, _ observable: T) where T: ObservableType, T.Element == Int {
    var subscriptionResult = "--"
    
    observable
        .subscribe(
            onNext: { value in
                subscriptionResult += " \(value) --"
        },
            onCompleted: {
                print("\(name) | \(subscriptionResult)>")
        })
        .disposed(by: disposeBag)
}

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

example(of: "elementAt",
    observable.elementAt(1)
)

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

elementAt      | -- 1 —>

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

Фильтрующие операторы

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

elementAt — оставляет только элемент указанного индекса и игнорирует все прочие элементы.

observable.elementAt(1)

Результат выполнения можно представить следующим образом (сверху оригинальная последовательность, снизу отфильтрованная):

Original       | -- 0 -- 1 -- 2 -- 3 -- 4 -- 5 -- 6 -- 7 -- 8 -- 9 -- 10 -->
elementAt      | -- 1 -->

filter — работает, как и одноименный оператор в Swift. Например:

observable.filter { $0 % 2 == 0 }

Результат выполнения:

Original       | -- 0 -- 1 -- 2 -- 3 -- 4 -- 5 -- 6 -- 7 -- 8 -- 9 -- 10 -->
filter         | -- 0 -- 2 -- 4 -- 6 -- 8 -- 10 —>

skip — позволяет пропустить заданное количество элементов последовательности. Можно реализовать более сложное поведение при помощи операторов skipWhile и skipUntil.

observable.skip(3)

Результат выполнения:

Original       | -- 0 -- 1 -- 2 -- 3 -- 4 -- 5 -- 6 -- 7 -- 8 -- 9 -- 10 -->
skip           | -- 3 -- 4 -- 5 -- 6 -- 7 -- 8 -- 9 -- 10 -->

take — работает противоположно skip, т.е. оставляет заданное количество элементов из последовательности. Есть варианты этого оператора takeWhile и takeUntil.

observable.take(2)

Результат выполнения:

Original       | -- 0 -- 1 -- 2 -- 3 -- 4 -- 5 -- 6 -- 7 -- 8 -- 9 -- 10 -->
take           | -- 0 -- 1 —>

distinctUntilChange - игнорирует дублирующиеся элементы до тех пор пока, не придет элемент отличный от предыдущего.

let elementsToDistinct = [0, 1, 1, 1, 2, 2, 1, 1,]
let distinctObservable = Observable.from(elementsToDistinct)
distinctObservable.distinctUntilChanged()

В результате получим:

Original       | -- 0 -- 1 -- 1 -- 1 -- 2 -- 2 -- 1 -- 1 -->
distinctUntil  | -- 0 -- 1 -- 2 -- 1 -->

Трансформирующие операторы

Трансформирующие операторы помогают совершать действия над элементами последовательность.

Самый простой и понятный пример трансформирующего оператора - map. Его принцип работы аналогичен одноименному оператору в Swift: он позволяет применить функцию к каждому из элементов последовательности.

observable.map { $0 * 10 }

Результат выполнения:

Original       | -- 0 -- 1 -- 2 -- 3 -- 4 -- 5 -- 6 -- 7 -- 8 -- 9 -- 10 -->
map            | -- 0 -- 10 -- 20 -- 30 -- 40 -- 50 -- 60 -- 70 -- 80 -- 90 -- 100 —>

reduce - тоже работает как одноименный оператор в Swift: применяет функцию последовательно к каждому транслированному значению, а затем возвращает финальный результат.

observable.reduce(0, accumulator: +)

Результат выполнения:

Original       | -- 0 -- 1 -- 2 -- 3 -- 4 -->
Original       | -- 5 -- 6 -- 7 -- 8 -- 9 -->
reduce         | -- 55 —>

Комбинирующие операторы

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

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

let elements1 = [0, 1, 2, 3, 4]
let elements2 = [5, 6, 7, 8, 9]

let observable1 = Observable.from(elements1)
let observable2 = Observable.from(elements2)

merge - объединяет элементы нескольких последовательностей.

Observable.merge(observable1, observable2)

Результат выполнения:

Original       | -- 0 -- 1 -- 2 -- 3 -- 4 -->
Original       | -- 5 -- 6 -- 7 -- 8 -- 9 -->
merge          | -- 0 -- 5 -- 1 -- 6 -- 2 -- 7 -- 3 -- 8 -- 4 -- 9 —>

concat - склеивает заданные последовательности в одну, при условии, что они завершились без ошибок.

Observable.concat(observable1, observable2)

Результат выполнения:

Original       | -- 0 -- 1 -- 2 -- 3 -- 4 -->
Original       | -- 5 -- 6 -- 7 -- 8 -- 9 -->
concat         | -- 0 -- 1 -- 2 -- 3 -- 4 -- 5 -- 6 -- 7 -- 8 -- 9 -->

combineLatest - объединяет 2 последовательности, применяя функцию для преобразования. В примере ниже мы перемножаем элементы двух последовательностей событий.

Observable.combineLatest(observable1, observable2, resultSelector: { $0 * $1 })

Результат выполнения:

Original       | -- 0 -- 1 -- 2 -- 3 -- 4 -->
Original       | -- 5 -- 6 -- 7 -- 8 -- 9 -->
combineLatest  | -- 0 -- 5 -- 6 -- 12 -- 14 -- 21 -- 24 -- 32 -- 36 -->

Сегодня мы рассмотрели только некоторые примеры операторов, оставив за кадром ещё большое количество, потому как я не ставил целью переводить документацию, а лишь продемонстрировал общий принцип. Настоятельно рекомендую ознакомиться со всеми группами и возможностями Rx-операторов по ссылке в начале этой статьи.

Исходный код примеров