Наверное, в каждом большом iOS-проекте — долгожителе можно наткнуться на иконки, которые нигде не используются, или обращения к ключам локализации, которые уже давно не существуют. Чаще всего такие ситуации возникают из-за невнимательности, а лучшее лекарство от невнимательности — автоматизация.  В iOS-команде HeadHunter мы большое внимание уделяем автоматизации рутинных задач, с которыми может столкнуться разработчик. Этой статьей мы хотим начать цикл рассказов о тех инструментах и подходах, которые упрощают нашу повседневную работу. Какое-то время назад нам удалось взять ресурсы приложения под контроль с помощью утилиты SwiftGen. О том, как ее настроить, как с ней жить и как эта утилита помогает переложить проверку актуальности ресурсов на плечи компилятора, и пойдет речь под катом.  SwiftGen — это утилита, которая позволяет генерировать Swift-код для доступа к различным ресурсам Xcode-проекта, среди них: Подобный код инициализации изображений или строк локализации мог писать каждый: Для обозначения названия изображения или ключа локализации мы используем строковые литералы. То, что написано между двойными кавычками, никак не валидируется компилятором или средой разработки (Xcode). В этом кроется следующий набор проблем: Давайте посмотрим, как мы можем улучшить подобный код с помощью SwiftGen. Для нашей команды была актуальна генерация только для строк и ассетов, про них и пойдет речь в статье. Генерация для остальных типов ресурсов аналогична и при желании легко осваивается самостоятельно. Для начала нужно установить SwiftGen. Мы выбрали его установку через CocoaPods как удобный способ распространения утилиты между всеми участниками команды. Но это можно сделать и другими способами, которые детально описаны в документации. В нашем случае все, что нужно сделать, — это добавить в Podfile pod 'SwiftGen', после чего добавить новую фазу сборки (Build Phase), которая будет запускать SwiftGen перед началом сборки проекта. Важно выполнять запуск SwiftGen перед запуском фазы Compile Sources, чтобы избежать ошибок при компиляции проекта.  Теперь можно приступить к адаптации SwiftGen под наш проект. Первым делом необходимо настроить шаблоны, по которым будет генерироваться код для доступа к ресурсам. Утилита уже содержит набор шаблонов для генерации кода, их все можно посмотреть на гитхабе и, в принципе, они готовы к использованию. Шаблоны пишутся на языке Stencil, возможно, вы с ним знакомы, если пользовались Sourcery или играли с Kitura. При желании каждый из шаблонов можно адаптировать под свои гайды.
Для примера возьмем шаблон, который генерирует enum для доступа к строкам локализации. Нам показалось, что в стандартном слишком много лишнего и его можно упростить. Упрощенный пример с поясняющими комментариями находится под спойлером. Сам файл шаблона удобно сохранить в корне проекта, например в папке SwiftGen/Templates, чтобы этот шаблон был доступен всем, кто работает над проектом.
Утилита поддерживает настройку через YAML-файл swiftgen.yml, в котором можно указать пути до исходных файлов, шаблонов и дополнительные параметры. Создадим его в корне проекта в папке Swiftgen, в эту же папку позже сгруппируем и другие файлы, связанные со скриптом.
Для нашего проекта этот файл может выглядеть так: По сути, там указаны пути до файлов и шаблонов, а также дополнительные параметры, которые передаются в контекст шаблона.
Так как файл лежит не в корне проекта, нам нужно указать путь до него при запуске Swiftgen. Изменим наш скрипт запуска: Теперь наш проект можно собрать. После сборки в папке проекта по указанным в swiftgen.yml путям должны появиться два файла Localization.swift и Image.swift. Их нужно добавить в Xcode-проект. В нашем случае сгенерированные файлы содержат следующее: Теперь можно заменить все использования строк локализации и инициализации изображений вида UIImage(named: "") на то, что у нас генерировалось. Это упростит нам отслеживание изменений в ключах строк локализаций или их удаление. В любом из этих случаев проект просто не соберется, пока все ошибки, связанные с изменениями, не будут исправлены.
После изменений наш код выглядит так: Есть одна проблема с генерируемыми файлами: их можно изменить вручную по ошибке, а так как они перезаписываются с нуля при каждой компиляции, эти изменения могут быть утеряны. Чтобы этого избежать, можно блокировать файлы на запись после исполнения скрипта SwiftGen.
Этого можно добиться с помощью команды chmod. Перепишем нашу Build Phase с запуском SwiftGen следующим образом: Скрипт получается довольно простой. Перед запуском генерации, если файлы существуют, мы выдаем для них права на запись. После выполнения скрипта мы блокируем возможность изменения файлов.
Для удобства редактирования и проверки скрипта на ревью удобно вынести его в отдельный файл runswiftgen.sh. Итоговый вариант скрипта с небольшими модификациями можно посмотреть здесь. Теперь наша Build Phase будет выглядеть следующим образом: на вход скрипту передаем путь до папки Pods: Пересобираем проект, и теперь при попытке изменения генерируемого файла вручную появится предупреждение:  Итак, папка со Swiftgen теперь содержит файл конфигурации, скрипт для блокирования файлов и запуска Swiftgen и папку с настроенными шаблонами. Удобно добавить ее в проект для дальнейшего редактирования при необходимости.  А так как файлы Localization.swift и Image.swift генерируются автоматически, их можно добавить в .gitignore, чтобы лишний раз не решать в них конфликты после git merge. Отдельно хочется обратить внимание, на необходимость указывать Input/Output Files для билд фазы SwiftGen, если в вашем проекте используется Xcode New Build System (она является системой сборки по умолчанию с Xcode 10). В списке Input Files необходимо указать все файлы, на основе которых генерируется код. В нашем случае это сам скрипт, конфиг, шаблоны, файлы .strings и .xcassets: В Output Files поместим файлы, в которых SwiftGen генерирует код: Указание этих файлов необходимо чтобы система сборки могла решить нужно ли запускать скрипт, в зависимости от наличия или отсутствия изменений в файлах, и в какой момент сборки проекта этот скрипт должен быть выполнен. SwiftGen — хороший инструмент для защиты от нашей невнимательности при работе с ресурсами проекта. С его помощью у нас получилось автоматически генерировать код для доступа к ресурсам приложения и переложить часть работы по проверке актуальности ресурсов на плечи компилятора, а значит, немного упростить нашу работу. Помимо этого, мы настроили проект Xcode, чтобы дальнейшая работа с инструментом была более удобной. Плюсы: Минусы: Полностью пример можно посмотреть на гитхабе iOS Developer