Вопрос проверяет умение распознавать архитектурные и качественные проблемы в UIKit-экранах, прежде чем они превратятся в технический долг.
Плохая структура обычно проявляется в слишком большом контроллере с разными обязанностями. Часто внутри смешаны UI, бизнес-логика, сеть и навигация. Методы становятся длинными, появляется дублирование и много условий. Тестировать такой код трудно, а изменения приводят к регрессиям. Хороший сигнал — когда контроллер «знает слишком много».
Плохая структура view controller обычно заметна по нескольким повторяющимся симптомам. Важно уметь их распознавать, чтобы рефакторинг был плановым, а не экстренным.
Перед тем как смотреть на размер файла, стоит оценить, что именно делает контроллер.
Типичные «лишние обязанности»:
Сеть внутри контроллера
URLSession/клиент API вызывается прямо из UIViewController.
обработка ошибок и ретраи тоже внутри.
Бизнес-логика
правила доступа, валидации, расчеты, фильтрация, сортировка.
Навигация
контроллер сам решает, куда и как переходить, собирает зависимости следующего экрана.
Работа с хранилищем
чтение/запись в Core Data/Realm прямо в UI-слое.
Почему это плохо:
контроллер становится точкой, где смешаны разные причины изменений;
любое изменение требований «цепляет» UI.
Частые маркеры:
методы на 50–200 строк;
сильная вложенность if/guard/switch;
обработка нескольких сценариев в одной функции.
Что это обычно означает:
отсутствует декомпозиция по сущностям и сценариям;
логика не оформлена отдельными объектами.
Признаки:
одинаковые куски кода в viewDidLoad, viewWillAppear, обработчиках кнопок;
похожая обработка ошибок в нескольких местах.
Риск:
исправление в одном месте не исправляет в другом;
быстро растет количество регрессий.
Контроллеры часто «ломаются», когда состояние экрана не описано явно.
Признаки:
много флагов: isLoading, isRefreshing, isFirstLoad, shouldShowEmptyState;
несогласованные переходы между состояниями;
UI обновляется точечно в разных местах (часто без единого центра управления).
Что помогает:
ввести единый state (например, loading/content/empty/error) и один метод рендера.
Пример идеи (коротко):
enum ScreenState {
case loading
case content([Item])
case empty
case error(String)
}
Перед тем как думать о тестах, посмотри на то, как создаются зависимости.
Признаки:
let api = ApiClient() внутри контроллера;
одиночки и глобальные менеджеры повсюду;
контроллер нельзя создать в тесте без реального окружения.
Последствия:
сложно писать unit-тесты;
сложно изолировать баги;
невозможна замена реализаций.
Плохая структура часто проявляется в неуправляемых асинхронных вызовах.
Признаки:
много nested completion handlers;
нет отмены при уходе с экрана;
UI обновляется после dismiss/pop, из-за чего возможны крэши;
отсутствие [weak self] и утечки памяти.
Что обычно делают:
выносят асинхронность в слой ViewModel/Presenter;
добавляют отмену и привязку к жизненному циклу.
Плохая структура view controller — это не «большой файл», а смешение обязанностей, неявное состояние и тяжелые зависимости. Если контроллер сложно тестировать, менять и читать — его нужно декомпозировать: вынести бизнес-логику, навигацию, работу с данными и привести управление состояниями к единой модели.