Функциональное реактивное программирование

Функциональное реактивное программирование

surf_mobile

Наверняка каждый iOS-разработчик, просматривая вакансии, не раз сталкивался с требованием: «Необходимо знание RxSwift и RxCocoa».

Эти фреймворки основаны на концепции реактивного программирования — подхода, который строится вокруг реакций на события. Например, пользователь взаимодействует с интерфейсом и ожидает мгновенного отклика от приложения. Такой стиль программирования популярен в мире фронтенда, включая iOS.

Ранее использование реактивного программирования в iOS-приложениях имело свои недостатки:

  • Лишние зависимости — сторонние библиотеки увеличивали сложность проекта.
  • Сложность поддержки — отладка и поддержка кода часто превращались в непростую задачу.

Но с появлением Combine и SwiftUI у нас больше нет необходимости прибегать к сторонним решениям. Теперь можно строить реактивные приложения, используя нативные инструменты Apple. Мы уже применяем подход и видим его преимущества.


Главные элементы Combine

Чтобы эффективно работать с Combine, важно разобраться в принципах реактивного программирования.

В основе Combine лежат четыре ключевых элемента:

  • Publisher — источник данных, который отправляет значения.
  • Subscriber — подписчик, который получает и обрабатывает данные.
  • Operators — операторы, позволяющие трансформировать потоки данных.
  • Subjects — особые сущности, объединяющие свойства Publisher и Subscriber.


Publisher (издатель)

В Combine ключевую роль играет Publisher — это протокол, который обозначает, что тип может передавать последовательность значений с течением времени.

Когда у Publisher есть подписчик (Subscriber), он отправляет ему данные по мере их появления. Но если подписчиков нет, Publisher остаётся неактивным и ничего не передаёт.

Publisher описывается двумя параметрами:

  • Output — тип данных, которые он отправляет.
  • Failure — тип ошибки, которая может возникнуть. Если ошибок не предполагается, используется Never.


Subscriber (подписчик)

Subscriber отвечает за получение данных от Publisher, а также за обработку возможных ошибок.

Как и Publisher, подписчик описывается двумя типами:

  • Input — тип данных, которые он принимает.
  • Failure — тип возможной ошибки.

Основная роль Subscriber — запрашивать данные и контролировать их объём при получении. Именно подписчик инициирует процесс передачи данных в реактивной цепочке.

В Combine у подписчика есть два способа обработать входящие данные:

sink(receiveCompletion:receiveValue:)

Метод принимает два замыкания:

  • Первое вызывается, когда передача данных завершена. В него передаётся Subscribers.Completion, который указывает, завершил ли Publisher работу успешно или вернул ошибку.
  • Второе выполняется при получении новых данных от Publisher.
assign(to:on:)

Этот метод немедленно присваивает каждое полученное значение указанному свойству объекта. Использует keyPath для указания свойства.


Operators (операторы) 

Операторы — это промежуточное звено между Publisher и Subscriber. Они позволяют обрабатывать, преобразовывать и фильтровать данные в потоке.

С помощью операторов можно:

  • Изменять значения, прежде чем передать их подписчику.
  • Фильтровать ненужные данные.
  • Комбинировать несколько потоков.
  • Управлять временем доставки данных.

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


Subjects

Subjects — это особый вид Publisher, который позволяет вручную отправлять данные подписчикам с помощью метода .send(_:).

Для чего используются Subjects:

  • Позволяют вводить значения в поток данных вручную.
  • Часто применяются для интеграции императивного кода в реактивную цепочку.

Как это работает:

  • Publisher передаёт данные, пока не завершится или не возникнет ошибка.
  • Если подписка больше не нужна, её можно отменить.

Как отменить подписку:

Все Subscriber реализуют протокол Cancellable. Он предоставляет метод .cancel(), который завершает подписку и останавливает получение данных.


Как можно применять Combine

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

  1. Создаётся Publisher, который оборачивает задачу получения данных для URL-запроса.
  2. Применяется оператор map. С помощью keyPath он извлекает свойство data и передаёт его в поток. Этот оператор похож на одноимённый оператор для массивов: позволяет преобразовывать входящие данные в другой тип.
  3. Происходит декодирование данных.
  4. Если на каком-то этапе произошла ошибка, она преобразуется в нужный тип с помощью mapError.
  5. Меняется очередь, на которой выполняются операции.
  6. Обрабатываются полученные данные.
  7. Cancelable экземпляр сохраняется в указанном сете. Если не сохранить, подписчик автоматически очистится после выполнения метода, и выполнение потока отменится. Чтобы этого не случилось, подписчик сохраняют в свойство класса.

Такой подход делает код более читаемым, управляемым и реактивным.


Полезные ссылки

Больше инсайтов мобильной разработки — в нашем Telegram-канале

Report Page