Back to questions
Senior
128
questionbank
Как вы переводили сущности с архитектуры NVC на Clean Swift?
Sobes Copilot
Get answers in real time
Answer from AI
sobes.tech AI
Переход от MVC к Clean Swift ( VIP) включает перераспределение ответственности из ViewController в Worker, Interactor, Presenter и Router.
Основные этапы:
- Идентификация сущностей и потоков: Определить, какие ViewController соответствуют каждому VIP-модулю и какие взаимодействия происходят.
- Создание шаблонов Clean Swift компонентов: Сгенерировать или создать вручную шаблоны для Interactor, Presenter, Worker, Router, Model и ViewController для каждого переводимого модуля.
- Рефакторинг ViewController: Удалить бизнес-логику и логику представления, оставляя только UI-код и код, взаимодействующий с Interactor и Router.
- Обработчики событий UI отправляют запросы в Interactor.
- ViewController обновляется на основе данных, полученных от Presenter.
- Перенос бизнес-логики в Interactor: Бизнес-правила, взаимодействие с Worker'ами и обработка входящих запросов переносятся в Interactor. Interactor вызывает Worker'ы для выполнения конкретных задач (сетевые запросы, работа с базой данных и т.д.).
- Перенос логики представления в Presenter: Преобразование данных из Interactor в формат, пригодный для отображения на UI. Presenter отправляет готовые данные во ViewController.
- Создание Worker'ов: Реализация конкретных операций (например, загрузка данных с сервера) в отдельных Worker-классах. Worker'ы вызываются из Interactor.
- Настройка Router: Определение переходов между модулями. Router отвечает за создание и настройку следующего VIP-модуля и передачу данных при переходе.
- Настройка связей: Установить связи между компонентами каждого VIP-модуля (например, ViewController содержит ссылку на Interactor, Interactor на Presenter и Worker'ы, Presenter на ViewController, Router содержит ссылки на Data Passing протоколы).
- Тестирование: Написание модульных и интеграционных тестов для каждого компонента (Interactor, Presenter, Worker) и проверка корректности работы всей системы.
Пример переноса логики загрузки данных:
В MVC:
// ViewController.swift
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData() {
// Сетевой запрос
NetworkService.shared.fetchData { [weak self] result in
switch result {
case .success(let data):
// Обработка данных
self?.displayData(data)
case .failure(let error):
// Обработка ошибки
self?.displayError(error)
}
}
}
func displayData(_ data: Data) {
// Обновление UI
}
func displayError(_ error: Error) {
// Отображение ошибки на UI
}
}
// NetworkService.swift
class NetworkService {
static let shared = NetworkService()
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
// Реализация сетевого запроса
}
}
В Clean Swift:
// MyViewControlller.swift (остается только UI и вызовы Interactor)
protocol MyDisplayLogic: AnyObject {
func displayData(viewModel: My.FetchData.ViewModel)
func displayError(viewModel: My.FetchData.ViewModel)
}
class MyViewController: UIViewController, MyDisplayLogic {
var interactor: MyBusinessLogic?
var router: MyRoutingLogic?
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
}
func fetchData() {
let request = My.FetchData.Request()
interactor?.fetchData(request: request)
}
func displayData(viewModel: My.FetchData.ViewModel) {
// Обновление UI на основе viewModel
}
func displayError(viewModel: My.FetchData.ViewModel) {
// Отображение ошибки на UI на основе viewModel
}
}
// MyInteractor.swift (бизнес-логика и взаимодействие с Worker)
protocol MyBusinessLogic {
func fetchData(request: My.FetchData.Request)
}
protocol MyDataStore {
// Какое-то состояние
}
class MyInteractor: MyBusinessLogic, MyDataStore {
var presenter: MyPresentationLogic?
var worker: MyWorker? // Worker для сетевых запросов
func fetchData(request: My.FetchData.Request) {
worker?.fetchData { [weak self] result in
switch result {
case .success(let data):
let response = My.FetchData.Response(data: data, error: nil)
self?.presenter?.presentData(response: response)
case .failure(let error):
let response = My.FetchData.Response(data: nil, error: error)
self?.presenter?.presentError(response: response)
}
}
}
}
// MyPresenter.swift (логика представления)
protocol MyPresentationLogic {
func presentData(response: My.FetchData.Response)
func presentError(response: My.FetchData.Response)
}
class MyPresenter: MyPresentationLogic {
weak var viewController: MyDisplayLogic?
func presentData(response: My.FetchData.Response) {
// Преобразование данных для ViewController
let viewModel = My.FetchData.ViewModel(displayData: "Processed: \(response.data!)", errorMessage: nil)
viewController?.displayData(viewModel: viewModel)
}
func presentError(response: My.FetchData.Response) {
// Преобразование ошибки для ViewController
let viewModel = My.FetchData.ViewModel(displayData: nil, errorMessage: "Error: \(response.error!.localizedDescription)")
viewController?.displayError(viewModel: viewModel)
}
}
// MyWorker.swift (конкретная операция - сетевой запрос)
class MyWorker {
func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
// Реализация сетевого запроса (аналог NetworkService в MVC)
}
}
// MyRouter.swift (логика навигации)
protocol MyRoutingLogic {
func routeToNextScreen()
}
class MyRouter: MyRoutingLogic {
weak var viewController: UIViewController?
func routeToNextScreen() {
// Реализация перехода
}
}
// MyModels.swift (структуры и протоколы для данных между компонентами)
enum My {
enum FetchData {
struct Request {}
struct Response {
let data: Data?
let error: Error?
}
struct ViewModel {
let displayData: String?
let errorMessage: String?
}
}
// Другие сценарии использования
}
Процесс требует тщательного анализа существующего кода и пошагового переноса логики в соответствующие слои Clean Swift, обеспечивая тестирование на каждом этапе. Использование шаблонов и инструментов Clean Swift может ускорить этот процесс.