Настройка архитектуры Clean Architecture для iOS-приложения

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1735 услуг
Настройка архитектуры Clean Architecture для iOS-приложения
Сложный
~3-5 дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    495

Настройка архитектуры Clean Architecture для iOS-приложения

Когда кодовая база iOS-приложения вырастает до 50–70 экранов, ViewController начинает отвечать и за сетевые запросы, и за трансформацию данных, и за навигацию. Тесты писать некуда — зависимости захардкожены. Новый разработчик открывает ProfileViewController.swift на 1200 строк и закрывает ноутбук.

Clean Architecture решает это через разделение на концентрические слои с жёстким направлением зависимостей: внутренние слои ничего не знают о внешних.

Как устроена Clean Architecture на практике в iOS-проекте

В классической трактовке Боба Мартина три кольца: EntitiesUse CasesInterface Adapters. В iOS это отображается следующим образом.

Domain-слой — ядро. Здесь Entity-модели: чистые Swift-структуры без импорта Foundation, только бизнес-данные. Рядом — UseCase-протоколы и их реализации. Например, FetchUserProfileUseCase принимает UserRepository через инъекцию зависимостей и возвращает AnyPublisher<UserProfile, DomainError>. Никакого URLSession, никакого CoreData. Этот слой компилируется и тестируется изолированно.

protocol UserRepository {
    func fetchProfile(id: String) -> AnyPublisher<UserProfile, DomainError>
}

final class FetchUserProfileUseCase {
    private let repository: UserRepository
    init(repository: UserRepository) { self.repository = repository }

    func execute(id: String) -> AnyPublisher<UserProfile, DomainError> {
        repository.fetchProfile(id: id)
    }
}

Data-слой — реализации репозиториев. UserRepositoryImpl работает с URLSession или Alamofire, маппит DTO → доменную модель, обрабатывает сетевые ошибки. CoreDataUserCache реализует тот же протокол для локального кеша. Выбор источника данных — в UserRepositoryImpl через стратегию или в DI-контейнере.

Presentation-слой — здесь живут ViewModel/Presenter. В связке с SwiftUI удобен ObservableObject-ViewModel: он вызывает UseCase, трансформирует результат в @Published-состояние и публикует его. ViewController или SwiftUI View занимается исключительно рендерингом.

Навигация: Coordinator или Router

Типичная проблема — ViewController создаёт следующий ViewController и делает push. Это нарушает Clean Architecture: presentation-слой знает о конкретных типах других экранов. Решение — Coordinator:

protocol ProfileCoordinator: AnyObject {
    func showEditProfile(user: UserProfile)
    func showOrders(userId: String)
}

ViewModel держит слабую ссылку на ProfileCoordinator. Конкретный ProfileCoordinatorImpl знает о UINavigationController и о следующих экранах. ViewModel — нет.

DI: чистая инъекция без Service Locator

Service Locator (глобальный DIContainer.shared.resolve()) — анти-паттерн: скрывает зависимости и ломает тесты. Используем инициализаторную инъекцию в цепочке: SceneDelegate создаёт AppCoordinator, тот — конкретные репозитории и UseCase-ы, передаёт их в ViewModel через init. Можно подключить Swinject или Needle, но для большинства проектов хватает ручной сборки в CompositionRoot.

Тестируемость — основной выигрыш

Domain-слой тестируется через XCTest без зависимости от UIKit: создаём MockUserRepository, подставляем в UseCase, проверяем логику. Никаких XCTestExpectation для сети, никаких моков URLSession.

final class FetchUserProfileUseCaseTests: XCTestCase {
    func test_execute_returnsProfile() {
        let mock = MockUserRepository(result: .success(.stub()))
        let sut = FetchUserProfileUseCase(repository: mock)
        var received: UserProfile?
        _ = sut.execute(id: "123").sink(
            receiveCompletion: { _ in },
            receiveValue: { received = $0 }
        )
        XCTAssertEqual(received?.id, "123")
    }
}

Время сборки тестов domain-слоя — секунды, не минуты. Это меняет культуру разработки в команде.

Типичные ошибки при внедрении

Слишком тонкие UseCase. GetUsernameUseCase, который делает return user.name — бессмысленный слой. UseCase оправдан, когда инкапсулирует нетривиальную логику или оркестрирует несколько репозиториев.

Domain-модели с Codable. Добавить Codable к доменной Entity означает протечку Data-слоя внутрь. DTO — в Data-слое, маппинг — там же.

ViewModel знает о конкретном репозитории. Если во ViewModel написано let repo = UserRepositoryImpl(...) — инъекции зависимостей нет. Только протокол + инициализаторная инъекция.

Что входит в настройку

Аудит текущей архитектуры (если проект существует): определяем, что выносится в Domain, что остаётся в Presentation, какие зависимости надо инвертировать.

Создание базовой структуры модулей: Domain, Data, Presentation — отдельные Swift Package или targets в одном Xcode-проекте. Настройка зависимостей между targets: Data зависит от Domain, Presentation зависит от Domain, не от Data.

Реализация CompositionRoot / DI-контейнера. Настройка первых 2–3 фича-модулей как образца для команды.

Написание базовых тестов domain-слоя как образца.

Сроки

Настройка архитектуры с нуля на новом проекте (структура + DI + первый модуль): 3–5 дней. Рефакторинг существующего проекта с миграцией 10–15 модулей: 2–4 недели в зависимости от объёма. Стоимость рассчитывается после анализа текущего кода и архитектурных решений.