Привет, читатель!
Ранее мы разобрали как устроен VIP цикл и как совершать переходы между с ценами с передачей данных. Теперь нужно разобраться как разгрузить наш Interactor от переизбытка логики и вынести ее часть для повторного использования другими сценами. И в этом нам помогут Worker’ы. Тема достаточно скромная по объему, но важная для упоминания.

Worker’ы — это вспомогательные классы/структуры (не путать с сервисами или хелперами) задача которых взять на себя часть бизнес логики Interactor’a. Если методы в Interactor’е разрастаются, значит пора выносить объемную логику в Worker. В Clean Swift их используют для работы с логикой хранения данных, с сетью, с отдельными слоями приложения и так далее. Другими словами — все объемное, низкоуровневое и не относящееся к бизнес логике приложения.
Worker’ы делятся на два основных типа:
Локальные Worker’ы размещаются непосредственно внутри сцены и именуются в ее честь — SceneNameWorker. Глобальные Worker’ы размещаются в корневой директории Workers и именуются под свою тематику. Так же локальные Worker’ы могут выступать как “декораторы над глобальными”, с расширенной логикой для нужд сцены.
В случае, если используется разделение проекта на слои (Presentation, Business Logic, Persistency, Network Logic), то роль соединяющего моста между слоем Presentation и Business Logic можно отдать Worker’ам. Таким образом мы разгрузим Interactor, получим более предсказуемое поведение и удобство в повторном использовании.
Работу Worker’ов мы рассмотрим на примере работы с сетью. У нас будет два экрана — на первом отображается список пользователей, а на втором список постов этого пользователя. Все данные будут браться по API. Эту задачу мы разобьем на три Worker’a, два локальный и один глобальный, который будет выступать точкой входа для двух других. Реализацию самих методов в статье я скрою, а кто захочет опробовать на практике, то в конце статьи будет ссылка на готовый проект.

Данная структура проекта не является эталонной для работы с сетью и никак не отображает того, как с ней нужно работать в Clean Swift. Все это сделано лишь для наглядного примера роли локальных и глобальных Worker’ов.
Для начала создадим глобальный Worker — NetworkWorker. Разместим его в директории Workers, на уровне с директорией Scenes. В примере ниже, есть метод sendRequest(to:params:completion), который будет являться общим для локальных Worker’ов. Он выполняет рутинную задачу — формирует ссылку из параметров, отправляет запрос и пересылает результат в completion.
Для первой сцены нам необходимо получить по API список всех пользователей. Для этого мы создаем локальный HomeWorker, который будет конфигурировать в себе параметры для загрузки пользователей и вызывать sendRequest(to:params:completion) в NetworkWorker с этими параметрами. Теперь в Interactor’е сцены нам нужно вызвать fetchUsers(completion:) и отправить полученные данные на обработку в Presenter.
При нажатии на ячейку таблицы, с именем пользователя, мы будем совершать переход и передачу выбранного пользователя на другую сцену.
На странице с постами пользователя мы создаем PostsWorker, но только с методом fetchPosts(userId:completed:). В него мы передаем ID пользователя, посты которого нужно загрузить. В методе формируем параметры и вызываем sendRequest(to:params:completion) в NetworkWorker’е. И по той же схеме, что и ранее, вызываем метод fetchPosts(userId:completed:) в Interactor’е сцены, передавая полученные данные в Presenter.
Теперь вся реализация у нас вынесена в отдельные файлы, которые можно повторно использовать, при этом не нагружая бизнес логику в Interactor’e.
Хоть Worker’ы очень просты и не раскрывают никаких потаенных знаний об архитектуре, все же их использование важно в Clean Swift. При написании Worker’ов стоит не забывать про протоколы, структурные паттерны и DI. Иначе у вас быстро образуется бардак из Worker’ов, куда по кусочкам выносилось все, что только можно было вынести.
Вот и все. Спасибо, что дочитали до конца, ниже есть ссылка на полный проект.
Ссылка на проект
Помощь в написании статьи: Bastien
Ранее мы разобрали как устроен VIP цикл и как совершать переходы между с ценами с передачей данных. Теперь нужно разобраться как разгрузить наш Interactor от переизбытка логики и вынести ее часть для повторного использования другими сценами. И в этом нам помогут Worker’ы. Тема достаточно скромная по объему, но важная для упоминания.

Теория
Worker’ы — это вспомогательные классы/структуры (не путать с сервисами или хелперами) задача которых взять на себя часть бизнес логики Interactor’a. Если методы в Interactor’е разрастаются, значит пора выносить объемную логику в Worker. В Clean Swift их используют для работы с логикой хранения данных, с сетью, с отдельными слоями приложения и так далее. Другими словами — все объемное, низкоуровневое и не относящееся к бизнес логике приложения.
Worker’ы делятся на два основных типа:
- Локальные, которые используются только внутри сцены
- Глобальные, которые доступны для любой сцены
Локальные Worker’ы размещаются непосредственно внутри сцены и именуются в ее честь — SceneNameWorker. Глобальные Worker’ы размещаются в корневой директории Workers и именуются под свою тематику. Так же локальные Worker’ы могут выступать как “декораторы над глобальными”, с расширенной логикой для нужд сцены.
В случае, если используется разделение проекта на слои (Presentation, Business Logic, Persistency, Network Logic), то роль соединяющего моста между слоем Presentation и Business Logic можно отдать Worker’ам. Таким образом мы разгрузим Interactor, получим более предсказуемое поведение и удобство в повторном использовании.
Практика
Работу Worker’ов мы рассмотрим на примере работы с сетью. У нас будет два экрана — на первом отображается список пользователей, а на втором список постов этого пользователя. Все данные будут браться по API. Эту задачу мы разобьем на три Worker’a, два локальный и один глобальный, который будет выступать точкой входа для двух других. Реализацию самих методов в статье я скрою, а кто захочет опробовать на практике, то в конце статьи будет ссылка на готовый проект.

Данная структура проекта не является эталонной для работы с сетью и никак не отображает того, как с ней нужно работать в Clean Swift. Все это сделано лишь для наглядного примера роли локальных и глобальных Worker’ов.
Для начала создадим глобальный Worker — NetworkWorker. Разместим его в директории Workers, на уровне с директорией Scenes. В примере ниже, есть метод sendRequest(to:params:completion), который будет являться общим для локальных Worker’ов. Он выполняет рутинную задачу — формирует ссылку из параметров, отправляет запрос и пересылает результат в completion.
struct NetworkWorker {
// MARK: - Private Properties
private let session = URLSession.shared
// MARK: - Public Methods
/// Глобальный Worker. Отправка запроса к API
///
/// - Parameters:
/// - to: Ссылка, на которую делать запрос
/// - params: Список параметров ссылки
/// - completion: Возвращает данные или ошибку
func sendRequest(to: URL, params: [String: String], completion: @escaping (Data?, Error?) -> Void) {
// ...
}
}
Для первой сцены нам необходимо получить по API список всех пользователей. Для этого мы создаем локальный HomeWorker, который будет конфигурировать в себе параметры для загрузки пользователей и вызывать sendRequest(to:params:completion) в NetworkWorker с этими параметрами. Теперь в Interactor’е сцены нам нужно вызвать fetchUsers(completion:) и отправить полученные данные на обработку в Presenter.
При нажатии на ячейку таблицы, с именем пользователя, мы будем совершать переход и передачу выбранного пользователя на другую сцену.
struct HomeWorker {
// MARK: - Private Properties
private let networkWorker = NetworkWorker()
private let usersURL = URL(string: "https://jsonplaceholder.typicode.com/users")
// MARK: - Public Methods
/// Локальный Worker. Запрос к API на загрузку списка пользователей
///
/// - Parameter complete: Возвращает загруженный список пользователей
func fetchUsers(_ complete: @escaping ([User]?) -> Void) {
// ...
}
}
На странице с постами пользователя мы создаем PostsWorker, но только с методом fetchPosts(userId:completed:). В него мы передаем ID пользователя, посты которого нужно загрузить. В методе формируем параметры и вызываем sendRequest(to:params:completion) в NetworkWorker’е. И по той же схеме, что и ранее, вызываем метод fetchPosts(userId:completed:) в Interactor’е сцены, передавая полученные данные в Presenter.
struct PostsWorker {
// MARK: - Private Properties
private let networkWorker = NetworkWorker()
private let postsURL = URL(string: "https://jsonplaceholder.typicode.com/posts")
// MARK: - Public Methods
/// Запрос к API на загрузку списка постов пользователя
///
/// - Parameters:
/// - userId: ID пользователя, посты которого нужно загрузить
/// - completed: Возвращает список загруженных постов
func fetchPosts(userId: Int, _ completed: @escaping ([Post]?) -> Void) {
// ...
}
}
Теперь вся реализация у нас вынесена в отдельные файлы, которые можно повторно использовать, при этом не нагружая бизнес логику в Interactor’e.
Заключение
Хоть Worker’ы очень просты и не раскрывают никаких потаенных знаний об архитектуре, все же их использование важно в Clean Swift. При написании Worker’ов стоит не забывать про протоколы, структурные паттерны и DI. Иначе у вас быстро образуется бардак из Worker’ов, куда по кусочкам выносилось все, что только можно было вынести.
Вот и все. Спасибо, что дочитали до конца, ниже есть ссылка на полный проект.
Серия статей
- Общее представление об архитектуре Clean Swift
- Router и Data Passing в архитектуре Clean Swift
- Workers архитектуры Clean Swift (вы здесь)
- Unit тестирование в архитектуре Clean Swift
- Пример простого интернет-магазина на архитектуре Clean Swift
Ссылка на проект
Помощь в написании статьи: Bastien