Сначала диагностика
Прежде чем что-то оптимизировать — нужно понять где именно теряется время. Xcode имеет встроенные инструменты для этого.
Добавьте в Build Settings проекта:
После следующей сборки в логах появятся строки с временем компиляции каждой функции. Ищите значения больше 100ms — это кандидаты на оптимизацию.
Ещё удобнее — плагин BuildTimeAnalyzer для Xcode. Показывает топ медленных мест в виде отсортированного списка.
Проблема 1: тяжёлый вывод типов
Компилятор Swift тратит время на вывод типов. Самые проблемные места — длинные цепочки вызовов и сложные литералы:
Проблема 2: избыточные import
Каждый import добавляет модуль в граф зависимостей. Особенно дорогой import Foundation в каждом файле — он тянет за собой сотни деклараций.
Проверьте каждый файл: действительно ли он использует всё что импортирует? В большом проекте часто находится 20-30% лишних импортов.
Проблема 3: монолитный таргет
Если весь код живёт в одном таргете, Swift перекомпилирует намного больше кода при каждом изменении чем нужно. Разбивка на модули даёт инкрементальную компиляцию на уровне модулей.
Наша схема после рефакторинга:
- Core — модели данных, протоколы, утилиты
- Networking — API клиент
- UI — переиспользуемые компоненты
- Features/X — отдельный модуль на каждый экран
- App — точка входа, только склейка
Про Whole Module Optimization
WMO (SWIFT_WHOLE_MODULE_OPTIMIZATION = YES) ускоряет финальный релизный билд, но замедляет debug-сборки. Не включайте его для Debug конфигурации — только для Release.
Кэш и DerivedData
Не удаляйте DerivedData без причины. Но если билд начал вести себя странно — удалите только папку конкретного проекта, не всю DerivedData целиком:
А что с CI?
На CI кэш DerivedData между сборками даёт огромный выигрыш. Если используете GitHub Actions — есть готовый action для кэширования. На Bitrise и CircleCI тоже есть встроенные механизмы.
Без кэша каждый билд на CI — холодный старт. С кэшем инкрементальный билд работает так же как локально.