Генерамба — кодогенератор для iOS разработки

image

TL; DR
Мы написали классный кодогенератор для iOS-разработки, обладающий следующими достоинствами:

  • Поддержка Swift и Objective-C,
  • Использование языка разметки liquid для создания шаблонов,
  • Гибкая система управления шаблонами,
  • Интеграция с менеджером зависимостей Cocoapods.


Больше подробностей — под катом.
Серьезные решения, касающиеся архитектуры проекта, несут за собой необходимость принятия определенного компромисса. Придерживаемся n-tier структуры — получаем некоторое количество пустых пробросов информации между слоями. Распараллеливаем задачи на несколько потоков — тратим огромное количество времени на решение неочевидных багов. В похожей ситуации, требующей принятия компромисса, столкнулись и мы в Rambler&Co, принимая решение использовать VIPER в качестве стандарта архитектуры всех наших мобильных приложений. Получив отличную модульность и четкое разделение ответственностей компонентов, мы приобрели головную боль в виде сложности и монотонности процесса создания новых модулей.

Среднестатистический iOS разработчик в начале работ над новым экраном просто создает один класс. Тот, кто принял волевое решение перейти на VIPER, в этот момент начинает страдать. В большинстве случаев ему требуется создать пять классов, шесть протоколов и написать пять тест-кейсов. Допустим, что для создания новых модулей наш бедолага наймет профессиональную секретаршу с огромной скоростью печати —, но даже в таком случае он вряд ли сможет выйти за рамки 30 секунд на создание и заполнение одного файла. Применим те небольшие знания математики, которыми наделила природа мобильных разработчиков, перемножим эти числа и получим ответ в районе 10 минут. Тот самый среднестатистический разработчик за это же время успеет накидать пару сотен строк UITableViewDataSource, отправить несколько сетевых запросов и покрасить все view«шки в красивый лазурный цвет. Как-то несправедливо по отношению к труду нашего VIPER-гуру.
f180a19d47a64fed9df8c0a6e1c300dc.jpeg
А ведь тяжелый и нудный ручной труд является не единственной проблемой. Не меньше головной боли приносят множащиеся с каждым модулем опечатки, которые с каждым днем все сильнее и сильнее утягивают проект на илистое дно сорванных сроков.
1bb33f171ef149fca47d9be7eb1fbb38.jpeg
Одним из способов решения указанных проблем, которым долгое время пользовались и мы — это создание своих собственных шаблонов для Xcode. Не считая того, что такой подход просто не спортивен, для себя мы выделили еще ряд относительно серьезных недостатков.

  • Xcode — не самая стабильная IDE, и периодически при его обновлениях шаблоны, плагины и прочий обвес могут успешно слетать.
  • Нет удобного механизма добавления новых шаблонов, не говоря уже о получении обновлений.
  • Принципиально отсутствует возможность добавления генерируемых файлов в разные таргеты проекта.
  • Синтаксис бесконечно неудобный и сложный, особенно для того, кому нужно за десять минут просто накидать шаблон для проекта.


Но, конечно, все понимают, что больше всего расстраивал еще один, фатальный, недостаток — решение написано не нами. Контроля над ним слишком мало, а расширяемость для более специфичных задач стремится к нулю. Поэтому мы и решили написать свой кодогенератор — Генерамбу. Сразу же уточню очень важный тезис — хоть мы и приступили к проекту для того, чтобы упростить процесс создания VIPER модулей, но в результате получили куда более гибкую утилиту, которая может помочь в автоматизации широкого спектра задач по генерации и стандартизации кода.

С момента установки Генерамбы (gem install generamba) до создания своего первого модуля нужно пройти три шага:

  • generamba setup запускает процесс настройки Генерамбы для работы с конкретным проектом. В этот момент задаются стандартные пути для файлов, настройки тестов, менеджеров зависимостей и прочей инфраструктуры. В результате выполнения команды мы получаем конфигурационный файл проекта — Rambafile.
  • generamba template install запускает процесс установки указанных в Rambafile шаблонов.
  • generamba gen HabrahabrModule rviper_controller уже непосредственно создает новый модуль HabrahabrModule, используя шаблон rviper_controller.


Для работы Генерамба использует несколько ресурсов:

  • Rambafile — конфигурационный файл, содержащий все, что пользователь указал во время выполнения команды setup, а также ссылки на шаблоны и их каталоги,
  • Один или больше шаблонов, которые могут быть указаны при создании нового модуля,
  • Пользовательские настройки (к примеру, имя автора).


Первые два пункта смело попадают под Git, а настройки, привязанные к пользователю, лежат в проекто-независимой директории.

Пример Rambafile
### Headers settings
company: Rambler&Co

### Xcode project settings
project_name: GenerambaSandbox
prefix: RDS
xcodeproj_path: GenerambaSandbox.xcodeproj

### Code generation settings section
# The main project target name
project_target: GenerambaSandbox

# The file path for new modules
project_file_path: GenerambaSandbox/Classes/Modules

# The Xcode group path to new modules
project_group_path: GenerambaSandbox/Classes/Modules

### Tests generation settings section
# The tests target name
test_target: GenerambaSandboxTests

# The file path for new tests
test_file_path: GenerambaSandboxTests/Classes/Modules

# The Xcode group path to new tests
test_group_path: GenerambaSandboxTests/Classes/Modules

### Dependencies settings section
podfile_path: Podfile
cartfile_path: Cartfile

### Templates
catalogs:
- 'https://github.com/rambler-ios/generamba-catalog'
- 'https://github.com/igrekde/my-own-catalog'
templates:
- {name: rviper_controller}
- {name: local_template_name, local: 'absolute/file/path'}
- {name: remote_template_name, git: 'https://github.com/igrekde/remote_template'}

Отдельного упоминания достойна работа с шаблонами. В отличии от многих других генераторов, мы не зашиваем шаблоны в саму утилиту — взамен этого мы встроили более гибкую систему, подсмотренную у менеджеров зависимостей (читай, Cocoapods). Новые шаблоны могут быть установлены с использованием одного из следующих путей:

  • Локальный шаблон (копируется из указанной папки)
  • Удаленный шаблон (клонируется из указанного репозитория)
  • Шаблон из каталога (в репозиториях с используемыми каталогами ищется подходящий шаблон по названию).


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

Как я уже упоминал, нас смущала сложность разметки стандартных шаблонов Xcode, и в качестве инструмента борьбы с этой проблемой был выбран шаблонный движок liquid — мало того, что с простым, удобным и понятным синтаксисом, так еще и с кучей дополнительных бонусов, которым можно найти применение не только во фронтенде, но и при кодогенерации.

Для сравнения, Xcode-шаблон:

InteractorTemplate.h
//
//  ___VARIABLE_viperModuleName______FILENAME___
//  ___PROJECTNAME___
// 
//  Created by ___FULLUSERNAME___ on ___DATE___
//  Copyright ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved.
//

#import "___VARIABLE_viperModuleName:identifier___Interactor.h"
#import "___VARIABLE_viperModuleName:identifier___InteractorOutput.h"

@implementation ___VARIABLE_viperModuleName:identifier___Interactor

#pragma mark - ___VARIABLE_viperModuleName:identifier___InteractorInput

@end


Liquid шаблон для такого же файла:

InteractorTemplate.liquid
//
//  {{ module_info.name }}{{ module_info.file_name }}
//  {{ module_info.project_name }}
//
//  Created by {{ developer.name }} on {{ date }}.
//  Copyright {{ year }} {{ developer.company }}. All rights reserved.
//

#import "{{module_info.name}}Interactor.h"
#import "{{module_info.name}}InteractorOutput.h"

@implementation {{module_info.name}}Interactor

#pragma mark - {{module_info.name}}InteractorInput

@end

Мы продолжаем активно развивать Генерамбу. Помимо относительно бытовых задач мы присматриваемся и к другим направлениям:

  • добавление GUI, в том числе в виде плагинов для IDE,
  • поддержка Android-проектов,
  • фантастика, требующая работы с AST-деревом — к примеру, автогенерация тест-кейсов по описанным протоколам или моков для swift-классов.


В качестве заключения хочется добавить, что Генерамба послужила отличным наглядным примером пользы автоматизации вполне тривиальных задач — полученный нами опыт мы перенесем и на другие области деятельности отдела, которым не помешало бы избавиться от ручного труда.

Решили начать использовать Генерамбу? Задавайте свои вопросы и пишите о найденных проблемах в issues — наше сообщество хоть и небольшое, но достаточно активное.

Полезные ссылки:

© Habrahabr.ru