От всех моих предыдущих реализаций сетевого слоя осталось впечатление, что есть еще куда расти. Данная публикация ставит целью привести один из вариантов архитектурного решения по построению сетевого слоя приложения. Речь пойдет не об очередном способе использования очередного сетевого фреймворка. Для начала из публикации 21 Amazing Open Source iOS Apps Written in Swift взято приложение Artsy. В нем используется популярный фреймворк Moya, на базе которого и построен весь сетевой слой. Отмечу ряд основных недостатков, которые встретил в данном проекте и часто встречаю в других приложениях и публикациях. Разработчик этим кодом обозначил некую логическую цепочку, в которой ответ на запрос activeAuctions преобразуется в массив объектов Sale. При повторном использовании этого запроса в других ViewModel или ViewController разработчику придется копировать запрос вместе с цепочкой преобразования ответа. Чтобы избежать копирования повторяющейся логики преобразования, запрос и ответ можно связать неким контрактом, который будет описан ровно один раз. Часто для работы с сетью используются фреймворки Alamofire, Moya и др. В идеале приложение должно минимально зависеть от этих фреймворков. Если в поиске по репозитоию Artsy набрать import Moya, то можно увидеть десятки совпадений. Если вдруг проект решит отказаться от использования Moya — очень много кода придется рефакторить. Не сложно оценить, на сколько каждый проект зависит от сетевого фреймворка, если убрать эту зависимость и попробовать доработать приложение до работоспособного состояния. Возможным выходом из ситуации с зависимостями будет создание специального класса, который будет один знать о фреймворках и обо всех возможных способах получить данные из сети. Эти способы будут описаны функциями со строго типизированными входящими и исходящими параметрами, что в свою очередь будет являться контрактом, упомянутом выше, и поможет справиться с проблемой повторов цепочек преобразования ответа. Такой подход тоже достаточно часто встречается. Его применение на практике можно также найти в приложениях из списка 21 Amazing Open Source iOS Apps Written in Swift. Например, в приложении DesignerNewsApp. Выглядит такой класс следующим образом: У такого подхода также есть минусы. Количество обязанностей, возложенных на этот класс больше, чем того требует принцип единственной ответственности. Его придется менять при смене способа выполнения запросов (замена Alamofire), при смене фреймворка для парсинга, при изменении параметров запроса. Кроме того, такой класс может перерасти в god object или использоваться как singleton со всеми вытекающими последствиями. Учитывая все недостатки, описанные в 1-й части публикации, я сформулировал для себя ряд требований к будущему слою работы с сетью: Что получилось в итоге: Протокол ApiTarget определяет все данные, которые необходимы для формирования запроса (параметры, путь, метод… и др.) Обобщенный протокол ApiResponseConvertible определяет способ преобразования полученного объекта (в данном случае Data) в объект связанного типа. Протокол ApiService определяет способ отправки запросов. Обычно функция, объявленная в протоколе, принимает замыкание содержащее объект ответа и возможные ошибки. В текущей реализации функция возвращает Observable — объект реактивного фреймворка RxSwift. Стратегией я называю упомянутый в начале публикации контракт, который связывает между собой несколько типов данных. Стратегия является протоколом и выглядит в самом простом случае таким образом: Для нужд сетевого слоя стратегия должна уметь создавать объект, который можно передать в экземпляр класса, соответствующего протоколу ApiService. Добавим функцию создания объекта в протокол ApiStrategy. Введение новой универсальной структуры AnyTarget обусловленно тем, что мы не можем использовать обобщенный протокол ApiResponseConvertible в качестве типа возвращаемого функцией объекта, потому что у протокола есть связанный тип.  Вот так выглядит самая примитивная реализация стратегии: Стоит отметить, что структура Target является приватной, т.к. за пределами файла использоваться она не будет. Она нужна лишь для инициализации универсальной структуры AnyTarget. Преобразование объекта тоже происходит в рамках файла, поэтому ApiService не будет ничего знать об инструментах, используемых при парсинге. Стратегия подскажет, какой объект нужен для осуществления запроса и какой объект будет на выходе. Все строго типизировано стратегией и не требуется указывать типы как в случае с универсальными функциями. Как можно было заметить, в данном подходе сетевой фреймворк остался за пределами основной логики построения сервиса. На первых порах его можно не использовать совсем. Например, если в реализации функции map протокола ApiResponseConvertible возвращать mock-объект, то сервис может быть совсем примитивным классом: Тестовую реализацию и применение протокола ApiService на базе реального сетевого фреймворка Moya можно посмотреть спойлере: Полученный сетевой слой сможет успешно существовать и без стратегий. Точно так же и стратегии можно применять для других целей и задач. Совместное же их использование сделало сетевой слой понятным и удобным в эксплуатации. iOS разработчик