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

    image

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

    Больше подробностей — под катом.

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

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

    А ведь тяжелый и нудный ручной труд является не единственной проблемой. Не меньше головной боли приносят множащиеся с каждым модулем опечатки, которые с каждым днем все сильнее и сильнее утягивают проект на илистое дно сорванных сроков.

    Одним из способов решения указанных проблем, которым долгое время пользовались и мы — это создание своих собственных шаблонов для 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 — наше сообщество хоть и небольшое, но достаточно активное.

    Полезные ссылки:
    • +10
    • 13,8k
    • 8

    Rambler Group

    80,00

    Компания

    Поделиться публикацией

    Похожие публикации

    Комментарии 8
    • НЛО прилетело и опубликовало эту надпись здесь
        +1
        Если имеете в виду OS X разработку, то да, тут никаких отличий нет.
        • НЛО прилетело и опубликовало эту надпись здесь
        +5
        Мне всегда казалось, что необходимость прибегать к такой кодогенерации — это не слишком хорошо. Одно дело, если сгенерированный код не нужно никак править и он просто используется, совсем другое дело, когда приходится генерировать тонны boiler plate заготовок только ради соблюдения шаблона проектирования. Как-то это сразу на темную сторону ведёт.
          +1
          эти «тонны кода» (на самом деле нет) очень хорошо помогают разложить проект по полочкам.
          Да и, в любом случае, попытки грамотно структурировать написанный код приведут к написаную не меньше объема кода.
          0
          А можно подробней про поддержку Android-проектов?
            0
            Есть желающий попробовать, пока все на уровне планов.
            0
            Когда будете пилить поддержку Android-проектов не забудьте про Kotlin, пожалуйста. В мире Java он как Swift в мире Objective-C.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое