1

Swift Combine - Часть 1 / 5 min

Серия Дневник разработчика

Это первая статья о работе со Swift Combine. Из неё вы узнаете о реактивном программировании и операциях преобразования, фильтрации и свертки сигналов.

В этой статье вы прочитаете:

  • Что такое Combine и реактивное программирование

  • Операции: Mapping, Filtering, Reducing

Примеры кода из этой статьи доступны на GitHub

Что такое Combine и реактивное программирование

Swift Combine - это интегрированная в язык Swift реализация реактивного программирования.

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

Пусть для простоты будут следующие операции:

  1. Получение ввода от пользователя, фильтрация 3х символов

  2. Формирование сетевого запроса для сервера и отправка запроса

  3. Расшифровка ответа

  4. Вывод результатов, сохраняя данные в переменную

В объектно-ориентированном императивном подходе на базе MVC у ViewController будет примерно так(Написал из головы):

В случае с реактивным программированием, это будет выглядеть примерно так:

Получается поток обработчиков, в которых нет "висячих" результатов работы. И прямой поток данных, и ветка ошибок должны иметь свой обработчик, иначе приложение не скомпилируется.

В целом плюсы реактивного подхода:

  1. Однозначность обработки положительных и отрицательных исходов

  2. Фильтрация событий по времени. В примере из всех вводов пользователя за 0.3 секунды будет взят последний

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

  4. Удобная связка со SwiftUI и Async благодаря тому, что Combine - часть языка, а не внешняя библиотека. Большая часть системных операций и UI также готова к работе с Combine

Источники данных и потребители

Эту тему я рассмотрю подробнее в будущей статье, но кратко опишу.

В Combine источники данных - это Publisher и дочерние типы. У основных системных вызовов, например у сетевых инструментов, SwiftUI и др. есть ответы и события в формате Publisher.

Результат работы можно назначить в свойство через метод assign или обработать как callback в методе sink. Они возвращают объект Cancellable, который надо сохрнить в переменной напрямую или через метод store, иначе память освободится и обработчик не будет работать.

Песочница для реактивных операций

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

Песочница доступа в Github со всеми примерами, включая будущие статьи

Операции и преобразования

Данные в Combine можно преобразовывать, менять потоки, фильтровать. Все они логически разбиты на блоки. Ниже я опишу каждый блок, входящие в него методы, что они делают и приведу диаграммы, показывающие вход и выход.

У некоторых операций есть try... вариант, который позволяет вернуть ошибку, но я их дополнительно не описывал.

Mapping

Код примеров

  • .map - преобразование данных, количество на входе равно количеству на выходе. Можно вернуть объект того же типа или поменять тип. Аналогично методу map массива. В примере звезды заменяются треугольниками.

  • .mapError - преобразование ошибок, ошибка на входе - ошибка на выходе. Позволяет свести все ошибки к одному типу для обработки в конце. Обработка в блоке. В примере ошибка меняется на зеленую ошибку.

  • .replaceNil - замена nil на фиксированный объект. В примере nil заменяется оранжевой звездой

  • .scan - похоже на map, но результат передается как второй параметр в блок обработки. На первый шаг подается первый аргумент вызова scan. В примере подается объект-образец, в цвет которого будут покрашены все элементы

  • .setFailureType - позволяет поменять тип ошибки для синхронизации типов. Never поменять на свой тип. В примере в коде сначала ошибка приводится к Never с помощью Catch, а затем обратно к DemoError с помощью setFailureType.

  • .map Keypath - аналогично map, но можно передать 1 keypath, чтобы получить на выходе значение. Если передать 2 или 3 keypath, будет tuple. В примере 1 keypath .color используется для создания результатов, а в примере 2 .color и .icon используются для создания копий в результатах

Filtering

Код примеров

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

  • .compactMap - аналгично map, но nil пропускается. В примере для сравнения map и compactMap на тех же данных

  • .removeDuplicates - удаляет одинаковые, идущие подряд элементы. Для этого они должны соответствовать протоколу Equitable. В примере синий и синяя звезда - разные.

  • .removeDuplicates(by: ) - удаляет одинаковые, идущие подряд элементы, определяя "одинаковость" в блоке. В примере одинаковость определяется оп цвету, В примере синий и синяя звезда - одно и то же.

  • .replaceEmpty - если поток закончился и ничего не вернул, то можно задать какое-то значение. В примере 1 возвращается оранжевый, а во втором были данные - возвращаются они

  • .replaceError - возвращает определенные данные, если была ошибка. В отличие от catch нет возможности обработать ошибку, она заменяется одним значением на входе

Reducing

Код примеров

  • .collect - собирает объекты в массив. В примере три цветных объекта становится одним массивом

  • .collect(n) - собирает объекты в массив по несколько штук. В примере 6 объектов собираются в две кучки по три. В примере два в кучки 4 и 2

  • .collect(.byTime ) - собирает объекты по несколько штук, пришедших в указанный интервал

  • .collect(.byTimeOrCount ) - также собирает объекты по несколько штук за указанный интервал, но не больше указанного количества

  • .ignoreOutput - гинорирует то, что было в выдаче. Вернет только сигнал окончания потока или ошибку

  • .reduce - работет как scan, но на выходе будет только 1 объект, последний. В примере суммируются числа на объектах

Заключение

В этой статье кратко рассказано что такое Swift Combine и основные операции преобразования, фильтрации и свертки сигналов с визуализацией. Все визуализации получены в песочнице.

В будущих статьях я расскажу про операции для работы с несколькими потоками вместе, про входы/выходы и примеры использования.

Все работающие примеры preview есть в проекте на Github, их можно скачать и запустить.

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

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества