Чистая архитектура в iOS: просто о сложном
Знаете, что общего между хорошей архитектурой приложения и идеальным гардеробом? В обоих случаях каждая вещь должна быть на своем месте, легко находиться и сочетаться с остальными. И если с одеждой все относительно просто (ну, по крайней мере, для тех, кто не пытается впихнуть весь гардероб в одну тумбочку), то с архитектурой приложения все несколько сложнее.

Когда мы говорим о Clean Architecture (Чистой архитектуре) в iOS-разработке, мы по сути говорим о том, как не превратить ваш проект в тот самый шкаф, где носки перемешались с документами, а старые джинсы почему-то лежат рядом с праздничным костюмом. Это философия разработки, предложенная Робертом Мартином (также известным как Дядя Боб – и нет, он не тот дядя, который продает барбекю).
Суть подхода проста – как в хорошо организованном офисе, где у каждого сотрудника есть своя зона ответственности, в чистой архитектуре каждый компонент занимается своим делом, не лезет в чужие дела и при этом эффективно взаимодействует с коллегами. Это особенно актуально сейчас, когда средний iOS-проект напоминает небольшой город со своей инфраструктурой, жителями (модулями) и правилами движения (зависимостями).
Давайте разберемся, как это работает на практике, и почему это может спасти вас от головной боли при масштабировании проекта. И да, обещаю – никаких скучных UML-диаграмм (ну, может быть, совсем чуть-чуть).
- Основные принципы чистой архитектуры
- Слои чистой архитектуры
- Применение чистой архитектуры в iOS
- Преимущества и недостатки чистой архитектуры
- Практические советы по внедрению чистой архитектуры в iOS-проектах
- Заключение
- Рекомендуем посмотреть курсы по обучению iOS разработчиков
Основные принципы чистой архитектуры
Знаете, что меня всегда забавляло в мире разработки? То, как мы любим все усложнять. Но принципы чистой архитектуры – это тот редкий случай, когда сложность действительно оправдана. Давайте разберем их, как говорится, не отходя от кассы (и желательно до того, как закончится кофе).
- Независимость от фреймворков Представьте, что ваше приложение – это космический корабль, а фреймворки – это двигатели. В чистой архитектуре мы строим корабль так, чтобы при необходимости могли заменить двигатели, не разбирая весь корабль. Звучит утопично? Возможно. Но когда в очередной раз придется мигрировать с одного фреймворка на другой (а такое случается чаще, чем хотелось бы), вы меня вспомните.
- Тестируемость О, это мой любимый пункт! Знаете эти приложения, где тестирование напоминает попытку собрать кубик Рубика в темноте? С чистой архитектурой каждый компонент можно тестировать отдельно. Да-да, прямо как в рекламе – «Отдельно тестируем, отдельно запускаем!»
- Независимость от UI UI в чистой архитектуре – как сменная обложка для телефона. Хотите Material Design? Пожалуйста! Решили перейти на что-то более минималистичное? Без проблем! Главное, что начинка телефона (бизнес-логика) остается неизменной.
- Независимость от базы данных «А что, если мы захотим перейти с CoreData на Realm?» – спросите вы. В чистой архитектуре это не проблема, а просто еще один вторник. Бизнес-логика даже не заметит подмены – как ребенок не замечает, что мама заменила пустую коробку из-под сока полной.
Конечно, все эти принципы звучат как манифест перфекциониста (которым я, признаюсь, являюсь). Но на практике они реально работают. Правда, только если вы готовы потратить время на правильную организацию кода – что-то вроде генеральной уборки, только в мире программирования.
И да, я знаю, что сейчас кто-то подумал: «Но это же так сложно!» Поверьте, сложнее будет потом объяснять заказчику, почему небольшое изменение в UI требует переписывания половины бизнес-логики. Кстати, об этом поговорим в следующем разделе – если, конечно, вы еще не убежали писать монолитное приложение.
Слои чистой архитектуры
Сущности (Entities)
Представьте, что ваше приложение – это большая корпорация. Сущности в этой аналогии – это совет директоров, те самые большие боссы, которые определяют основные правила игры. Они не знают (и не должны знать), как именно секретарша Маша сортирует входящие документы или какой кофемашиной пользуются в офисе.
В iOS-разработке сущности – это классы и структуры, определяющие самые важные бизнес-правила. Например, в приложении для управления задачами сущностью будет Task со своими неизменными правилами: задача не может иметь отрицательный приоритет или быть завершенной до своего создания (хотя иногда очень хочется, правда?).
struct Task { let id: UUID let title: String let priority: Int let creationDate: Date var completionDate: Date? // Бизнес-правила прямо в структуре - почему бы и нет? func isValid() -> Bool { return priority >= 0 && (completionDate == nil || completionDate! > creationDate) } }
И помните – сущности должны быть чистыми, как слеза младенца. Никаких UIKit-импортов, никаких зависимостей от базы данных. Только бизнес-логика, только хардкор!
Сценарии использования (Use Cases)
Если сущности – это совет директоров, то сценарии использования (или интеракторы, как их часто называют) – это менеджеры среднего звена. Они знают, как выполнить конкретную задачу, используя правила, установленные «сверху».
Например, когда пользователь хочет создать новую задачу, UseCase берет на себя всю грязную работу:
class CreateTaskUseCase { private let taskRepository: TaskRepositoryProtocol init(repository: TaskRepositoryProtocol) { self.taskRepository = repository } func execute(title: String, priority: Int) -> Result<Task, Error> { let task = Task( id: UUID(), title: title, priority: priority, creationDate: Date() ) // Проверяем бизнес-правила guard task.isValid() else { return .failure(TaskError.invalidTask) } // Если все ок - сохраняем return taskRepository.save(task) } }
Интересный факт: хороший UseCase похож на хорошего менеджера – делает ровно одну вещь, но делает её хорошо. Никаких «а давайте заодно и котиков из интернета подгрузим» – только четкая бизнес-логика для конкретного сценария.
И да, здесь уже можно использовать протоколы для зависимостей – мы же не в каменном веке живем. Но только через абстракции, никаких конкретных реализаций! Как в той поговорке: «Доверяй, но проверяй через протокол».
Адаптеры интерфейса (Interface Adapters)
Представьте адаптеры как профессиональных переводчиков в нашей корпоративной аналогии. Они берут сухой язык бизнес-логики и переводят его на язык, понятный пользователям и внешним системам.
class TaskPresenter { weak var viewController: TaskDisplayLogic? func presentTask(_ task: Task) { // Преобразуем сущность в то, что понятно UI let viewModel = TaskViewModel( title: task.title, priorityText: formatPriority(task.priority), dateText: formatDate(task.creationDate) ) viewController?.displayTask(viewModel) } private func formatPriority(_ priority: Int) -> String { // Превращаем числовой приоритет в понятный текст return priority > 5 ? "🔥 Срочно" : "😴 Можно не спешить" } }
В этом слое живут презентеры, контроллеры и всякие репозитории – короче, все те ребята, которые умеют говорить на двух языках: языке бизнес-логики и языке внешнего мира. Как дипломаты, только вместо международных конфликтов решают проблемы взаимодействия между слоями приложения.
И да, здесь уже можно импортировать UIKit – но только если вы действительно уверены, что вам это нужно (спойлер: обычно не нужно).
Фреймворки и драйверы (Frameworks and Drivers)
Это самый «внешний» слой архитектуры – своего рода «зона контакта» с реальным миром. Здесь обитают все эти UIKit’ы, CoreData, Alamofire и прочие создания, без которых современная разработка была бы… ну, скажем так, менее приятной.
class CoreDataTaskRepository: TaskRepositoryProtocol { private let context: NSManagedObjectContext init(context: NSManagedObjectContext) { self.context = context } func save(_ task: Task) -> Result<Task, Error> { let taskEntity = TaskEntity(context: context) // Маппинг из домейн-модели в CoreData taskEntity.id = task.id taskEntity.title = task.title do { try context.save() return .success(task) } catch { return .failure(error) } } }
Забавно, но этот слой часто напоминает мне песочницу – здесь можно играть со всеми внешними зависимостями, но только по строгим правилам: никакого песка за пределы песочницы! Вся «грязная» работа с фреймворками должна оставаться здесь и не просачиваться в внутренние слои.
И да, именно здесь можно (и нужно) использовать все эти модные SPM-пакеты. Только не увлекайтесь – помните, что каждая новая зависимость это потенциальная головная боль при обновлении.
Применение чистой архитектуры в iOS
Структура проекта
А теперь давайте поговорим о том, как все это богатство организовать в реальном проекте. Помните, как в детстве родители заставляли раскладывать игрушки по полочкам? Так вот, здесь примерно то же самое, только вместо игрушек – код.
MyAwesomeApp/ ├── Domain/ │ ├── Entities/ │ │ └── Task.swift │ └── UseCases/ │ └── CreateTaskUseCase.swift ├── Data/ │ ├── Repositories/ │ │ └── TaskRepository.swift │ └── Network/ │ └── APIClient.swift └── Presentation/ ├── ViewModels/ │ └── TaskViewModel.swift └── Views/ └── TaskViewController.swift
Знаете, что самое забавное? Когда вы покажете такую структуру джуниору, он скажет: «Зачем так сложно? Давайте все в один MVC-файл засунем!» А через полгода, когда проект разрастется до размеров небольшой операционной системы, он же первый скажет спасибо за такую организацию.
Каждая папка здесь – как отдельная квартира в многоэтажке. Domain – это пентхаус, где живет важная бизнес-логика. Data – это технический этаж со всеми коммуникациями. А Presentation – это первый этаж с красивыми витринами для пользователей.
И да, можно организовать структуру и по-другому – главное, чтобы потом можно было найти нужный файл быстрее, чем носки в шкафу.

На скриншоте представлен репозиторий GitHub с примером реализации Clean Architecture и MVVM в iOS-приложении. В левой части отображается структура проекта, включающая подпроекты (ExampleMVVM, ExampleMVVMTests, MVVM Templates) и вспомогательные файлы (README.md, Gemfile, .gitignore). Правая часть содержит описание проекта: упомянуто, что используется Clean Architecture с такими элементами, как DIContainer, FlowCoordinator и SwiftUI-компоненты. Также отображаются теги, популярность репозитория (звёзды, форки) и ссылка на техническую статью.
Взаимодействие между слоями
В чистой архитектуре слои общаются между собой как в хорошей бюрократической системе – строго по протоколу и только через официальные каналы связи.
// Протокол для взаимодействия с репозиторием protocol TaskRepositoryProtocol { func save(_ task: Task) -> Result<Task, Error> func fetch() -> Result<[Task], Error> } // UseCase использует репозиторий через протокол class TaskUseCase { private let repository: TaskRepositoryProtocol private let presenter: TaskPresenterProtocol init(repository: TaskRepositoryProtocol, presenter: TaskPresenterProtocol) { self.repository = repository self.presenter = presenter } func createTask(title: String) { let task = Task(title: title) switch repository.save(task) { case .success(let savedTask): presenter.presentTask(savedTask) case .failure(let error): presenter.presentError(error) } } }
Здесь работает главное правило: зависимости всегда направлены внутрь. Внешние слои знают о внутренних, но не наоборот – примерно как в армии, где генералы не обязаны знать имя каждого рядового.
И помните – никаких «коротких путей»! Даже если очень хочется напрямую дернуть базу данных из UI, держите себя в руках. Чистая архитектура – это как диета: либо вы ее соблюдаете, либо нет.
Примеры кода
// Domain Layer struct User { let id: UUID let name: String } // Use Case Layer protocol UserRepository { func getUser(id: UUID) -> User? } class GetUserUseCase { private let repository: UserRepository init(repository: UserRepository) { self.repository = repository } func execute(userId: UUID) -> User? { return repository.getUser(id: userId) } } // Presentation Layer class UserViewModel { private let useCase: GetUserUseCase init(useCase: GetUserUseCase) { self.useCase = useCase } func loadUser(id: UUID) { if let user = useCase.execute(userId: id) { // Обновляем UI } } }
Преимущества и недостатки чистой архитектуры
Преимущества
- Тестируемость Каждый компонент можно тестировать отдельно – как в хорошем ресторане, где каждое блюдо пробуют перед подачей. UseCase’ы, презентеры, репозитории – все тестируется независимо и без боли.
- Масштабируемость Добавление новых фич становится похоже на сборку LEGO – берете нужные блоки, соединяете по правилам, и всё работает. Никакого «а давайте впихнем это в существующий контроллер, там еще есть место».
- Поддерживаемость Когда приходит новый разработчик в проект, он не кричит «КТО ЭТО НАПИСАЛ?!» каждые 5 минут. Код организован логично, каждый компонент на своем месте.
Недостатки
- Сложность старта Настройка проекта с чистой архитектурой – как сборка мебели из IKEA: вроде и инструкция есть, но все равно придется помучиться. Особенно если это ваш первый опыт.
- Избыточность для малых проектов Для небольшого приложения чистая архитектура может быть как канонада из пушки по воробьям. Иногда простой MVC вполне достаточно (только не говорите об этом архитектурным пуристам).
- Много кода Придется писать больше кода – протоколы, маппинги, адаптеры. Да, это делает код чище и понятнее, но иногда хочется просто сделать app.getData() и получить результат.
И помните: архитектура должна помогать, а не мешать. Если вы ловите себя на мысли «зачем я вообще это делаю?», возможно, стоит упростить подход.
Практические советы по внедрению чистой архитектуры в iOS-проектах
Пошаговое руководство
- Начните с Domain Layer
// Определите основные сущности struct User { let id: UUID let name: String let email: String } // Создайте протоколы репозиториев protocol UserRepository { func getUser(id: UUID) -> User? func saveUser(_ user: User) -> Result<User, Error> }
- Определите Use Cases
class CreateUserUseCase { private let repository: UserRepository init(repository: UserRepository) { self.repository = repository } func execute(name: String, email: String) -> Result<User, Error> { let user = User(id: UUID(), name: name, email: email) return repository.saveUser(user) } }
- Организуйте слои представления
- Создайте ViewModels для каждого экрана
- Определите протоколы для View Controllers
- Настройте DI-контейнер (если используете)
Главное правило при переходе: не пытайтесь перевести весь проект сразу. Двигайтесь небольшими итерациями, начиная с самого важного функционала.
Инструменты и библиотеки
- DIKit или Swinject для dependency injection (потому что писать свой DI-контейнер — это как изобретать велосипед в 2024 году)
- XCTest для юнит-тестирования (встроенный фреймворк, но от этого не менее полезный)
- Quick и Nimble для более удобного тестирования (если вам надоели стандартные XCTest assertions)
- SwiftLint для поддержания консистентности кода (потому что без него ваш красивый Clean Architecture может превратиться в грязный беспорядок)
Причем помните: инструменты должны помогать, а не создавать дополнительные проблемы. Не стоит тащить в проект всё, что блестит на GitHub.
Заключение
В мире iOS-разработки чистая архитектура – это как хороший фундамент для дома. Да, его строительство требует времени и усилий, но зато потом можно спокойно достраивать этажи, не боясь, что всё рухнет.
Давайте подведем итоги:
- Чистая архитектура помогает создавать масштабируемые приложения
- Каждый слой имеет свою чётко определенную ответственность
- Тестирование становится не головной болью, а рутинной процедурой
- Новые разработчики могут быстрее влиться в проект
Конечно, это не серебряная пуля (которых, как мы знаем, в программировании не существует). Но это надёжный инструмент, который при правильном использовании может значительно упростить жизнь команде разработки.
В конце концов, хорошая архитектура – это инвестиция в будущее вашего проекта. И хотя поначалу может казаться, что это лишняя работа, поверьте моему опыту – оно того стоит.
Если вы заинтересовались чистой архитектурой и хотите углубить свои знания в iOS-разработке, рекомендую обратить внимание на специализированные курсы. На странице лучших курсов по iOS-разработке вы найдете подборку образовательных программ разного уровня сложности, многие из которых включают углубленное изучение архитектурных паттернов, в том числе Clean Architecture. Это отличный способ структурировать свои знания и получить практический опыт под руководством опытных разработчиков.
Рекомендуем посмотреть курсы по обучению iOS разработчиков
Курс | Школа | Цена | Рассрочка | Длительность | Дата начала | Ссылка на курс |
---|---|---|---|---|---|---|
iOS-разработчик
|
Eduson Academy
58 отзывов
|
Цена
Ещё -14% по промокоду
140 000 ₽
400 000 ₽
|
От
5 833 ₽/мес
0% на 24 месяца
16 666 ₽/мес
|
Длительность
7 месяцев
|
Старт
12 мая
Пн,Ср, 19:00-22:00
|
Ссылка на курс |
iOS-разработчик с нуля
|
Нетология
42 отзыва
|
Цена
с промокодом kursy-online
125 001 ₽
208 334 ₽
|
От
3 472 ₽/мес
Это кредит в банке без %. Но в некоторых курсах стоимость считается от полной цены курса, без скидки. Соответственно возможно все равно будет переплата. Уточняйте этот момент у менеджеров школы.
6 111 ₽/мес
|
Длительность
13 месяцев
|
Старт
19 мая
|
Ссылка на курс |
iOS-разработчик
|
Яндекс Практикум
87 отзывов
|
Цена
202 000 ₽
|
От
15 500 ₽/мес
На 2 года.
|
Длительность
10 месяцев
Можно взять академический отпуск
|
Старт
3 июня
|
Ссылка на курс |
iOS-разработчик
|
GeekBrains
68 отзывов
|
Цена
с промокодом kursy-online15
132 498 ₽
264 996 ₽
|
От
4 275 ₽/мес
|
Длительность
1 месяц
|
Старт
13 мая
|
Ссылка на курс |
Профессия Мобильный разработчик
|
Skillbox
128 отзывов
|
Цена
Ещё -33% по промокоду
175 304 ₽
292 196 ₽
|
От
5 156 ₽/мес
Без переплат на 31 месяц с отсрочкой платежа 6 месяцев.
8 594 ₽/мес
|
Длительность
8 месяцев
|
Старт
7 мая
|
Ссылка на курс |

Сертификация тестировщиков: обзор возможностей и рекомендаций
Сертификация тестировщиков становится всё более значимой в IT-индустрии. В статье вы узнаете о популярных программах, таких как ISTQB и CMST, их уровнях и особенностях, а также о том, как выбрать подходящий сертификат для профессионального роста.

Командная динамика под контролем: как работать с хаосом, а не бояться его
Модель Такмана объясняет, почему в начале проекта всё кажется запутанным и напряжённым. Эта статья поможет определить, на какой стадии ваша команда — и как ей помочь перейти к продуктивности.

SwiftUI: Простота и гибкость в одном фреймворке
SwiftUI — это фреймворк нового поколения, который заменяет сложные подходы к созданию интерфейсов декларативным методом. Разберем, как он работает и в чем его преимущества для разработчиков.

Как сделать портфолио дизайнера интерьера, которое привлечет клиентов
Хорошее портфолио — это не просто красивые картинки, а ваша визитная карточка. Разберем, как правильно оформить работы, выбрать формат и продвигать себя.