Создаем шаблон Xcode проекта

    Всем привет, я Ваня — iOS-разработчик. В этой статье я расскажу о том, как создавать Xcode шаблоны проектов и о том, как они помогли сэкономить время на старте проекта.



    Сейчас у 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
    Компания

    Комментарии 5

      0
      Возникло пару вопросов:
      1. Как часто вы начинаете проект, если реально понадобился шаблон?
      2. Неужели проекты настолько однотипны, что нужен шаблон? Это касается в частности Podfile
        0
        1. Больше 5 раз в год (+проекты для онбординга создаем при помощи этого же шаблона)
        2. Какие-то базовые вещи, типа Crashlytics есть во всех проектах
        0
        Не совсем понял конкретный минус использования xcodegen. Можно поподробнее если не трудно?
          0

          Чтобы создать проект с 0 и затянуть нужные зависимости к XcodeGen нужно будет дополнительно навесить каких-то скриптов, которые добавят нужные нам файлы

            0
            А каких именно файлах идет речь?

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

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