
Сейчас у iOS-разработчиков много средств для автоматизации рутинных действий. В Surf мы используем Generamba, Fastlane, SwiftGen, SwiftLint, Jenkins. И постоянно ищем пути автоматизировать что-то ещё. И если раньше на инициализацию нового проекта мы тратили 1-2 дня, теперь на это уходит не больше 4-х часов.
Что нужно чтобы начать разработку большого проекта?
1. Создать репозиторий.
— Настроить права доступа;
— Настроить уведомления в чат разработчиков.
2. Создать проект.
— Создать структуру папок;
— Добавить различные вспомогательные файлы вроде всеми любимых extensions;
— Настроить CI/CD;
— Добавить различные линтеры и генераторы (Generamba/SwiftLint/SwiftGen);
— Подтянуть зависимости;
—…
Спустя 1–2 дня работы можно приступать к полноценной разработке.
Очевидно, что большинство из этих шагов можно автоматизировать. Из возможных вариантов под рукой были:
— XcodeGen;
— Шаблоны Xcode проектов;
— Базовый Xcode проект.
XcodeGen
+ Позволяет генерировать .xcodeproj на лету при помощи файла конфигурации, при помощи чего добавляет возможность избавиться от конфликтов в проектном файле.
– Используется в связке с другими скриптами, так как работает только с проектным файлом, не затрагивая остальные файлы.
Шаблоны Xcode проектов
+ Можно генерировать .xcodeproj со всеми настройками, а также любые другие дополнительные файлы.
– Нет полноценной официальной документации и описание шаблонов в xml.
Базовый Xcode проект
+ Быстро настроить и сразу со всеми нужными файлами и зависимостями.
– Переделывать под конкретный проект долго. А еще можно случайно пропустить шаг и выстрелить себе в ногу.
В итоге мы остановились на использовании Xcode проектов, это позволило оптимизировать процесс и сократить время на создание проекта. Базовый Xcode проект показался слишком костыльным решением, а гибкость, которую привносят кастомные скрипты, нам пока не нужна.
Шаблоны Xcode проектов
Как я писал выше, при помощи Xcode шаблонов можно создавать iOS/macOS/tvOS/watchOS/Cross-platform проекты и добавлять любые файлы и настройки. Проблема в том что Apple не предоставляет никакой документации для этого. Всё что есть — пара небольших туториалов и примеры разных энтузиастов, я пользовался вот этой wiki.
Что есть из коробки
Шаблоны iOS приложений находятся тут:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/Project Templates/iOS/
а для MacOS приложений тут:
/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/Project Templates/Mac/
Шаблоны проектов представляются обычными .plist файлами. Вот как выглядит привычный Single View Application.

Каждый шаблон имеет уникальное имя — «Identifier», шаблоны от Apple имеют com.apple.dt.unit префикс.
«Ancestors» — родители текущего шаблона. Шаблоны поддерживают множественное наследование, то есть Single View Application наследует свойства у «Storyboard Application» и «Core Data Cocoa Touch Application».
Вот так выглядит Иерархия Single View Application.

Создадим свой шаблон
Пользовательские шаблоны лучше добавлять в локальную библиотеку ~/Library/Developer/Xcode/Templates, так мы их не потеряем при обновлении Xcode. Если такой директории нет — создайте её.
Я подготовил небольшой шаблон, который будет добавлять в наш проект Podfile с прописанными заранее зависимостями.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Kind</key>
<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
<key>Identifier</key>
<string>ru.surfstudio.dt.unit.customTemplate</string>
<key>Ancestors</key>
<array>
<string>com.apple.dt.unit.singleViewApplication</string>
</array>
<key>Concrete</key>
<true/>
<key>Description</key>
<string></string>
<key>Definitions</key>
<dict>
<key>../Podfile</key>
<string>platform :ios, '11.0'
pod 'Alamofire'
pod 'Crashlytics'
pod 'Fabric'
</string>
</dict>
<key>Nodes</key>
<array>
<string>../Podfile</string>
</array>
</dict>
</plist>
Здесь мы создали секцию Definitions, где будут содержаться переменные и файлы, которые можно добавить в проект. Туда и добавили ключ Podfile. Ключ указывает файл, который создаем или редактируем. После ключа задаем строку, которая запишется в этот файл. Вместо прописывания строк вручную, ссылаемся на файлы из каталога с шаблоном и берем их оттуда.
Теперь возьмем этот код и положим в файл с названием TemplateInfo.plist в папку CustomTemplate.xctemplate из директории с шаблонами.
Посмотрим, что у нас получилось. Если все пошло по плану, то при создании проекта в Xcode (File->New->Project (⇧⌘N)) мы увидим новую секцию Templates и в ней наш новый шаблон «CustomTemplate».

Выберем его и создадим проект. После создания добавится Podfile, как нам и нужно.

Прокачиваем шаблон
Давайте добавим генерацию Readme для нашего проекта. Теперь попробуем добавлять его сразу файлом, а не описывать внутри шаблона.
Итак, добавим файл README.md внутрь нашего CustomTemplate.xctemplate и туда вот это:
# ___PROJECTNAME___
Description of my project
Для того чтобы наш шаблон смог добавлять файл в секцию Nodes нужно дописать:
<string>../README.md</string>
Также, внутрь секции Definitions добавляем:
<key>../README.md</key>
<dict>
<key>Path</key>
<string>README.md</string>
</dict>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Kind</key>
<string>Xcode.Xcode3.ProjectTemplateUnitKind</string>
<key>Identifier</key>
<string>ru.surfstudio.dt.unit.customTemplate</string>
<key>Ancestors</key>
<array>
<string>com.apple.dt.unit.singleViewApplication</string>
</array>
<key>Concrete</key>
<true/>
<key>Description</key>
<string></string>
<key>Definitions</key>
<dict>
<key>../Podfile</key>
<string>
platform :ios, '11.0'
pod 'Alamofire'
pod 'Crashlytics'
pod 'Fabric'
</string>
<key>../README.md</key>
<dict>
<key>Path</key>
<string>README.md</string>
</dict>
</dict>
<key>Nodes</key>
<array>
<string>../Podfile</string>
<string>../README.md</string>
</array>
</dict>
</plist>
Давайте проверим, что из этого получилось.
Попробуем снова создать проект при помощи нашего CustomTemplate, и в итоге мы увидим, что в проект добавился Readme:

Внимательные заметили, что в README.md в заголовк мы добавляли ___PROJECTNAME___, а не «MyNewProject». ___PROJECTNAME___, это предопределенная константа, которую можно использовать для своих нужд.
Например, для того чтобы сгенерировать заголовок для файла с кодом, как это делает за нас обычно Xcode, воспользуйтесь следующим шаблоном:
//
// ___FILENAME___
// ___PACKAGENAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
Еще больше информации о доступных константах есть тут.
Заключение
Сегодня мы научились создавать простые шаблоны проектов. Теперь у нас есть шаблон, который умеет добавлять Podfile и README файлы в наш проект.
Если вы хотите пойти по нашим стопам и сократить время на создание проекта, то рекомендую:
1. Выбрать подходящее решение.
Кроме Xcode шаблонов есть и другие: XcodeGen или базовый проект. В любом случае, перед выбором инструмента, изучите плюсы и минусы и выберите подходящий.
2. Детально описать действия необходимые для создания типового проекта. Проекты инициируются не очень часто, поэтому можно забыть какие-нибудь шаги.
3. Попробовать автоматизировать шаги из пункта #2.
Например, часть из них перенести в Xcode шаблон, а часть в скрипты для вызова после создания проекта.
Полезная информация: обзорная статья по шаблонам и доки по шаблонам
Кейсы, лучшие практики, новости и вакансии Surf — в нашем телеграм-канале Surf iOS Team. Присоединяйтесь >>