В подавляющем большинстве современных мобильных приложений используется сетевой обмен данными. Обладая обширным опытом сетевого взаимодействия в крупных компаниях (банки, маркетплейсы итп), хотим поделиться опытом построения идеального, с нашей точки зрения, сетевого клиента для iOS. В нашем представлении идеальный REST клиент обеспечивает: Сетевые запросы в одну строчку в большинстве случаев Асинхронность (с iOS 13.0) Гибкость (возможность широкой настройки) Компактность реализации. Это значит, что код клиента легко понять и, в случае обнаружения недостатка, доработать под свои нужды. В нашем случае код клиента умещается в ~300 строк, протокол с расширениями в ~200 строк и два инструментальных файла по 30 строк. Всего 4 файла суммарно < 600 строк. Пример нашего типичного сетевого запроса (внутри некоего класса): Описание сущности userSession будет далее. Дано: У нас есть стандартный класс URLSession. Требуется: Построить REST клиент поверх HTTP c обменом данными в формате JSON. Обеспечить возможность автоматической реакции на некоторые ошибки, чтобы не реагировать на них вручную в каждом обработчике сетевого запроса. Это свойство называется Request Retrier. Оно полезно в случае, когда с сервера в ответе на любой запрос может прилететь приоритетное прерывание, без которого дальнейшая работа невозможна (например, обновление пользовательского соглашения об оказываемых услугах которое необходимо принять).  Обеспечить возможность извлекать из сетевых ответов общую информацию и, в некоторых случаях, кидать исключения. Это свойство называется Response Validator. Случается, что сервер, в случае ошибки запроса, присылает не 400-е значения ошибки, а 200 и, вместо ожидаемого ответа, информацию об ошибке запроса. Именно для обработки таких ситуаций применяется ResponseValidator. URLSession, обладая обширными возможностями и универсальностью, по нашему мнению,  обладает некоторыми недостатками. Перечислим их: Классический способ подразумевает получение данных через параметр-замыкание в то время как сейчас предпочтительным является асинхронный результат. Начиная с iOS15 в URLSession появилась поддержка асинхронности.  Наше решение работает начиная с iOS13. Возвращаемый результат представляет собой класс типа Data и URLResponse которые надо вручную анализировать и разбирать для получения целевых Decodable структур DTO (Data Transfer Object) - структур обмена данными или получения информации о транспортной ошибке. Так же в запросе надо указывать параметры заголовка и класть в них всякую техническую информацию вроде токенов аутентификации (к которым, разумеется, должен быть доступ в месте сетевого вызова). Можно (и нужно) эту информацию положить один раз в URLSessionConfiguration перед инициализацией URLSession. Но можно про это и не знать, и каждый раз указывать это в сетевом запросе. Да, URLSession настолько гибкий что можно один и тот же результат получать несколькими различными способами, и часто разработчики идут самым прямолинейным путем. Ограничимся рассмотрением самого популярного фреймворка Alamofire. Плюсы: Популярный фреймворк, хорошо подходит для простых проектов. Минусы: Заявленная универсальность приводит к громоздким конструкциям "в несколько строк"  Большой объём кода самой библиотеки (300кб). Хочется иметь представление о всём коде, который мы используем. В случае Alamofire это довольно затруднительно. Иногда отсутствует поддержка полезных фич (etag). Её очень долго не было и рекомендация поддержки была "не используйте etag". В последних версиях исправили. Миграция - только одна версия Alamofire на проект. Мажорные версии существенно отличаются. Миграция в большом проекте из многих модулей, которые ведут разные команды представляется затруднительной. Особенно затруднительна миграция при смене парадигм сетевых запросов (синхронная с замыканиями / асинхронная), поскольку эта смена влечет переделку всего сервисного кода приложения. Проблема "длинной истории" разработки. Alamofire разрабатывается многие годы, за которые в нём накопилось огромное количество разного редко-используемого функционала, что повлекло существенную громоздкость  библиотеки. Новые особенности там появляются в довольно неуклюжем виде. Как в асинхронном примере выше.  Сравните с самым первым примером кода Сетевой запрос является естественно-асинхронной операцией. С появлением в iOS 13 асинхронных вызовов они идеально подошли для реализации сетевого обмена. Сетевой клиент у нас реализован в два уровня: протокол и реализация. Дизайн протокола отражает желательный характер сетевых запросов. То есть, GET, POST, PUT, DELETE, PATCH HTTP запросы. На первый взгляд, тут присутствует некоторая похожесть методов, но при использовании это выглядит максимально естественно и удобно. Помимо этого, протокол позволяет замокать реализацию для написания модульных-тестов. Протокол AsyncHttpClient дополняет расширение, которое позволяет: Не указывать параметры методов кроме URL. Не возвращать значение для всех методов кроме GET. В каждом методе протокола AsyncHttpClient присутствует опциональный набор тюнеров, который позволяет в случае необходимости как угодно настроить любой запрос. В этом проявляется максимальная гибкость нашего решения. Если оно поверх URLSession/URLRequest, значит должен быть опциональный доступ и к URLSession и к URLRequest. Пример использования тюнера: Обратим внимание на параметр метода get parameters: . Для удобства кодирования параметров поставляется протокол CompactDictionaryRepresentable, который транслирует swift-структуру в формат . При этом, все опциональные отсутствующие поля игнорируются. Пример: Метод get поддерживает etag. Для этого кодирование параметров в URL query всегда происходит в одном и том же порядке. В случае недостаточности функционала, имеется доступ к базовой URLSession чтобы иметь возможность использовать её функционал. Функционал HTTP-клиента реализует класс Он конфигурируется URLSessionConfiguration где должны быть настроены параметры аутентификации. При смене параметров аутентификации подразумевается пересоздание всего HTTP-клиента. Дополнительно можно указать обработчик исключений (requestRetrier), проверщик ответов (responseValidator) и формировщик дат. К сожалению, стандартный формировщик дат ISO8601DateFormatter обладает недостатками, по этому в пакет включен расширенный формировщик дат, позволяющий работать с временными поясами и дробными секундами. В случае если responseValidator кинет исключение, то requestRetrier тоже вызовется. Подробно описывать код сетевого клиента представляется излишним. Его технологическая часть составляет примерно 100 строк. Ссылка на код находится в ссылках. В дополнение к сетевому клиенту рекомендуется создать класс UserSession, который будет представлять пользовательскую сессию. Например: Реализация опущена, т.к. она незначительная для нашей статьи. Именно такую сессию использует код из примера 1 Также рекомендуется вместо непосредственного обращения к сессии (и сетевому клиенту) из бизнес-логики приложения использовать промежуточный сервисный слой. Он изолирует сущности бизнес логики от сущностей сетевого транспорта и исправляет транспортные артефакты вроде таких: Для наглядности, визуализируем рекомендованную архитектуру сервисного слоя: В данной статье мы описали реализацию идеального, по нашему мнению, сетевого клиента на iOS (и прочих AppleOS), которая отличается удобством использования, функциональной гибкостью и компактностью реализации. Надеемся, эта статья окажется полезной для читателей. Пишите комментарии, задавайте вопросы.  Код асинхронного сетевого клиента на GitHub Моя статья "Идеальный наблюдатель на Swift"