На пути к «10x инженеру»: шорткаты, сниппеты, шаблоны
Программирование — творческая работа. Но в ней много рутины: поиск, перемещение и редактирование текста (читай кода), создание файлов, написание часто повторяющихся конструкций. Рутина — часть рабочего дня, но тратит время и выматывает — такое точно счастья не приносит.
Примерно полтора года назад мы стали практиковать изучение шорткатов, хоткеев, сниппетов и файловых шаблонов на уровне iOS-команды «Додо Пиццы». Мы распечатывали листы с расширенными командами (базовые уже знали) и держали их под рукой, чтобы учить. Это вошло в практику и теперь, когда к нам в команду приходят новые ребята, мы тренируем команды во время онбординга (и порой сами узнаем что-то новое). Наш опыт и все команды, которые мы изучали, описал в статье: вы узнаете как использовать не только базовые шорткаты для работы в Xcode, чем могут помочь кастомные сниппеты и как прокачать файловые шаблоны. Получилась «методичка» по командам в Xcode, которая поможет iOS-разработчикам работать эффективнее. Добавляйте в закладки, чтобы посмотреть при случае (а навигация в статье поможет быстро найти то, что нужно).
Оглавление
— Шорткаты и хоткеи
— Навигация
— Редактирование
— Отладка
— Кастомизация
— Сниппеты
— Шаблоны
— Бонус фича
Шорткат или хоткей?
Шорткат — то, что дает быстрый доступ к пункту меню (копировать/вставить/удалить).
Хоткей — то, у чего нет аналога в пункте меню/интерфейсе взаимодействия (кастомный биндинг).
Строгой разницы между этими терминами нет, можно смело использовать их как взаимозаменяемые. Но я постараюсь придерживаться тех определений, которые описал выше.
Использование шорткатов трудно переоценить: попробуйте вспомнить, когда вы последний раз копировали что-либо через контекстное меню (ПКМ → Копировать). Эти операции делаются на автомате. Давайте рассмотрим, какие еще операции мы можем довести до автоматизма при работе в Xcode.
В этой статье рассмотрим шорткаты для UIKit-flow.
Базовые клавиши для активации шорткатов:
- ⌘ — command;
- ⌥ — option;
- ⌃ — control;
- ⇧ — shift.
Базовые шорткаты:
- ⌘ + B — build;
- ⌘ + R — run;
- ⌘ + U — run unit tests;
- ⌘ +. — stop build/run/run unit tests;
- ⌘ + ⇧ + ⌥ + K — clean build folder.
Уверен, большинство из вас знакомы с ними, поэтому перейдём к интересному. Шорткаты разбиты на три блока: навигация, редактирование и отладка.
Навигация
Начнём автоматизацию рутины с шорткатов для навигации:
⌘ + 0 (ноль) — показать/скрыть панель Navigator
:
⌘ + цифры — быстрое перемещение по вкладкам панели Navigator
:
Повторим трюк с панели навигации для панели Inspectors
и научимся скрывать/показывать её. Просто добавляем option: ⌥ + ⌘ + 0 (ноль):
С зажатым option мы также можем пройтись по всем вкладкам панели — ⌥ + ⌘ +цифры:
Assistant Editor
скрыть не получиться, зато в нём можно делать много крутых штук.
⌘ + ⇧ + O — быстро найдем и откроем файл. Открыть можно не только файлы проекта, но и файлы с документацией iOS:
Если найденный файл хочется открыть рядом, зажимаем ⌥, перед тем, как нажать Enter
:
Либо, можно зажать ⌥ + ⇧ — в этому случае мы сами выбираем, где необходимо расположить радактор:
Перемещаться между редакторами можно с помощью сочетания ⌘ + J + стрелочки для выбора:
- ⌘ + ⌃ + T — позволяет открыть несколько редакторов (зажмите столько, сколько нужно);
- ⌘ + ⌃ + ⌥ + T — меняет ориентацию для открываемого редактора (если предыдущий редактор открывался горизонтально, то новый откроется вертикально, и наоборот);
- ⌘ + ⇧ + J — поможет быстро найти файл в структуре проекта.
- ⌘ + T — добавить новую вкладку;
- ⌘ + W — закрыть текущую вкладку;
- ⌘ + ⇧ + \ — посмотреть все вкладки.
Рассмотрим навигацию внутри панели, отображающую путь до файла:
Все взаимодействия с ней происходят путём зажатия ⌃ и цифры.
Для всех сочетаний данной панели (за исключением ⌃ + 1) вы можете использовать поиск: после того как сработал хоткей, начните писать название файла/класса. Так работать с данной панелью гораздо удобнее.
⌃ + 1 — позволяет узнать о суперклассах нашего класса, какие протоколы он поддерживает, кто вызывает его методы, какие экстеншены есть у класса.
- ⌃ + 2 и ⌃ + 3 — отображает список файлов, по которым мы перемещались. Эти сочетания отлично дополняют два других: ⌃ + ⌘ + стрелочки влево/вправо для непосредственной навигации по истории просмотренных файлов назад и вперёд;
- ⌃ + 4 — открывает окно для навигации по структуре проекта (мало чем полезно, на мой взгляд, часто проекты имеют большую вложенность);
- ⌃ + 5 — навигация по файлам внутри директории, в которой открыт текущий файл;
- ⌃ + 6 — навигация по свойствам и методам класса в текущем файле.
Идем дальше:
- ⌘ + L — переход к строке кода;
- ⌘ + курсор на миникарте — показать структуру файла.
⌃ + ⌥ + ⌘ + Enter — открыть класс для сториборда в отдельном редакторе:
⇧ + ⌃+ ⌥ + ⌘ или ⇧ + ⌥ + ⌘ + ПКМ — контекстное меню иерархии вьюх в сториборде:
Редактирование
- ⌘ + стрелки влево/вправо — позволяют быстро перейти в начало/конец строки;
- ⌥ + стрелки влево/вправо — быстрое перемещение между словами без пробелов;
- Control + стрелки влево/вправо — перемещение между словами в
camelCase
.
Здесь есть хитрость: изначально эта комбинация в macOS переключает рабочие столы, поэтому для работы хоткея в Xcode, его нужно переопределить, либо изменить в System Preferences.
Вот что мы сможем делать в итоге:
Все эти сочетания отлично работают с зажатым Shift для выделения. А вот что дальше можно делать с этим выделенным блоком:
⌥ + ⌘+ [ или ] — перемещает выделенный блок/строчку вверх/вниз. Самая крутая фича здесь — автоматический заход в скоупы.
- ⌘ + [ или ] — перемещает выделенный блок (строку) влево/вправо;
- ⌃ + I — автоматическое выравнивание выделенного блока (согласно настройкам в Xcode → Preferences → Text Editing → Indentations);
- ⌥ + выделение курсором — такая комбинация создает множественные курсоры. Удобно проставлять атрибут доступа в модельке или добавлять/удалять одинаковые части.
- ⌘ + / — закомментировать/раскомментировать текущую строку;
- ⌃ + ⌘ + E — редактировать выделенную сущность (но не забудьте поправить конструктор, его шорткат не затрагивает).
- ⌘ + F — поиск по файлу;
- ⌘ + ⇧ + F — поиск по проекту;
- ⌥ + ⌘ + F — поиск и замена;
- ⌃ + ⌥ + ⌘ + F — фикс всех ошибок в скоупе (не совсем то, о чем вы подумали, но тоже очень крутая вещь). В данном примере явно указывается модификатор доступа
public
.
⇧ + ⌘ + A — показать контекстное меню действий. В зависимости от того, для кого мы вызываем это меню, Xcode предложит релевантные действия. В списке также можно использовать поиск:
Отладка
Я заметил, что в нашей команде мало кто пользуется шорткатами для консоли, а ведь это не самое редкое место в работе. Давайте изучать.
Брейкпойнты:
- ⌘ + \ — добавить/удалить брейкпойнт;
- ⌘ + Y — отключить (disable) брейкпойнт.
Консоль:
- ⌘ + ⇧ + c — открыть консоль;
- ⇧ + ⌥ + Y — скрыть консоль.
Навигация внутри консоли:
- ⌘ + K — очистить содержимое консоли;
- s — step into (step);
- n — step over (next);
- finish — step out;
- c — continue;
- breakpoint disable — отключить все брейкпойнты.
В итоге работа с дебагером будет выглядеть примерно вот так:
Кастомизация
В настройках Xcode можно задать свои биндинги для операций. Например, зададим хоткей для оборачивания строки в NSLocalizedString
. Для этого перейдем во вкладку Key Bindings
настроек Xcode и найдем интересующую команду:
У себя в команда мы решили что это будет сочетание ^ + ⌘ + L. Можем смело использовать:
Или добавить хоткей для удаления строки целиком — ⌘ + D (⌘ + backspace удаляет только текст):
Возможно не самое очевидное сочетание для такой операции, но использовать удобно:
Можно не только задавать, но и изменять существующие биндинги. Например, зачем мне шорткат для печати в Xcode? Удалил его, теперь можно забиндить на него что-то полезное.
Xcode позволяет создать несколько биндинг сетов. Можно создать сет и пошарить его в команде, это позволит более эффективно работать в паре. Файлы биндингов расположены здесь: ~/Library/Developer/Xcode/UserData/KeyBindings
Наша команда на этом не остановилась —, а что если забиндить запуск скриптов, например для установки/переустановки подов? В Xcode, в отличие от AppCode, нет встроенного терминала, приходится переключаться между вкладками. Получается, чтобы установить/переустановить поды в проекте, необходимо переключиться на терминал, перейти в папку с проектом и прописать команду. Ужасно долго. Но решение есть!
Воспользуемся другой функциональностью Xcode — Behaviors
:
- добавим поведение через + внизу слева;
- зададим хоткей для вызова (как раз один освободился);
- укажем путь до скрипта.
В нашем случае скрипт выглядит так: запускаем терминал (если он не запущен), переходим в папку с проектом и можем указывать нужные команды для работы.
#!/bin/sh
osascript <
Теперь можно пользоваться терминалом не выходя из Xcode: ⌘ + P запустит скрипт на переустановку. Зажать сочетание — это всё, что от нас нужно.
Какие еще разделы для шорткатов можно прокачать:
- рефакторинг — extract to function/method/variable, add missing switch case;
- брейкпойнты — создание Swift Error/Exception/Symbolic/Test Failure;
- Version Control Source — push/pull/fetch/new branch/checkout;
- работа с Swift Package Manager — add/reset/resolve/update.
Как изучать шорткаты в программах? Как минимум, все стандартные приложения macOS имеют вкладку Help с возможностью поиска. Поиск в интерактивном режиме не только проведет вас по всем самым потаённым местам, но и подскажет шорткат.
Сниппеты
Иногда, работая над проектом, я понимал, что раз за разом пишу одно и то же — так называемый бойлерплейт. Этот процесс тоже можно автоматизировать. К счастью, сейчас есть много очень хороших решений и они скорей всего вам знакомы: это и Sourcery для генерации кода по шаблону и библиотеки по типу R.Swift для генерации доступа к ресурсам, и другие фреймворки. Но не всегда есть возможность затащить стороннюю зависимость в проект. К тому же есть бойлерплейт-код, где подобные библиотеки не помогут. Зато поможет стандартный инструмент Xcode — сниппеты.
Сниппеты есть для всех ключевых слов языка Swift (и некоторых других) и стандартных конструкций: циклов, ветвлений, функций. Но часто мне нужен не просто var
, а private/public var
. Точно также мне не нужен просто @testable
, мне нужен @testable import
. Другими словами, я бы хотел оперировать уже готовыми конструкциями, а не собирать их по частям.
Изменить стандартные сниппеты нельзя, зато добавить свой совсем просто: выделяем блок кода и в контекстном меню выбираем «Create Code Snippet». Дальше Xcode предложит его настроить: задать имя, добавить описание, платформу, скоуп вызова, и, конечно же, указать комплишн, по которому будет вызываться наш сниппет:
Вот так быстро можно «сгенерировать» код для минимальной работы таблицы (ещё быстрее можно сделать, если сразу в сниппете задать количество строк и возвращать пустую ячейку).
Так выглядит сниппет в библиотеке Xcode (⌘ + ⇧ + L):
Как добавить плейсхолдер: поместите ваш текст внутри конструкции <# MyDescription #>
(лучше добавлять её уже после того, как вы добавили описание, так как она сразу «схлопывается»).
А вот как в два тапа можно сделать импорт целой пачки фреймворков для юнит-тестов:
В проекте «Додо Пиццы» мы пишем тесты с помощью Quick/Nimble. Каждый новый спек начинается с импорта 4–6 фреймворков (минимум), и продолжается телом разного уровня вложенности. Импортировать фреймворки мы уже научились, сделаем это и для тела спеки. Всего пара тапов по клавиатуре и сетап для тестов готов:
Еще один кейс: в нашем проекте для логирования ошибки необходимо описать её в специальном виде: создаем энумку, наследуемся от протокола Error
, задаем кейсы, устанавливаем код ошибки, описываем различные параметры. Каждый раз по памяти писать неудобно:
Для меня это идеальный кандидат для оптимизации своего времени — пусть сниппет делает все за меня:
А сколько еще таких мест можно улучшить (и всё это бесплатно). Несколько личных примеров кандидатов для сниппета:
- Делегат и data source для таблицы/коллекции (обязательные методы).
- При объявлении методов протокола срабатывал стандартный сниппет функции (с открытыми фигурными скобками). Но мне не нужны скобки в протоколе, поэтому я добавил свой сниппет, без скобок. Теперь не нарадуюсь.
Часто типы данных и функции нужно объявлять с явным типом доступа. Это можно можно завернуть в сниппет.
- weak self для замыканий — guard let weakSelf = self else { return };
- локализация — оборачивание в NSLocalizedString (основное отличие от хоткея описанного выше в том, что здесь мы указываем параметр для бандла).
Шаблоны
Плавно продолжаем тему кодогенерации кода, но теперь поднимемся на уровень выше — шаблоны файлов.
Шаблоны удобны, но Apple не предоставляет инструменты для их разработки и документации. Но можно сделать все ручками, потому что есть доступ к стандартным шаблонам файлов (и проектов) Xcode. Чтобы их найти, перейдём в каталог по этому пути:
// ⌘ + ⇧ + G в Finder
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates/Source/Cocoa Touch Class
Дальше разместим свои темплейты: создаём папку по пути ниже, её имя будет отображаться в окне Xcode. Я назвал папку Helpers:
~/Library/Developer/Xcode/Templates/Files Templates/Helpers
Шаблон для Xcode — это каталог, куда входят:
- файл шаблона (например,
.swift
или.storyboard
); .plist
файл с настройками;- иконки двух размеров для отображения в окне Xcode при создании файла.
Чтобы создать свой шаблон, скопируем папку со стандартным шаблоном Xcode и изменим его под себя. Например, для создания объекта API реквеста у себя в проекте мы используем определенную структуру, где описываем стандартные для запроса вещи: урл, параметры, заголовки.
Часто нам интересны только пара параметров, но всё остальное мы обязаны прописать (обязанность протокола). Как раз для этого случая я создал шаблон файла, который не только экономит мне время, но и сразу подсказывает где и что дописать.
Вот так может выглядеть .plist
файл в нашем случае:
http://www.apple.com/DTDs/PropertyList-1.0.dtd">
Platforms
com.apple.platform.iphoneos
Kind
Xcode.IDEFoundation.TextSubstitutionFileTemplateKind
Description
Internet request
Summary
Internet request
SortOrder
1
AllowedTypes
public.swift-source
DefaultCompletionName
File
MainTemplateFile
___FILEBASENAME___
Options
Default
Some
Description
Name of the request
Identifier
productName
Name
Request name:
NotPersisted
Yes
Required
Yes
Type
text
Добавил код в .swift
файл (полный пример на GitHub) и обновил иконки. Теперь можно протестировать создание в Xcode (возможно потребуется его перезапуск). Появилась отдельная секция Helpers с моим шаблоном.
А вот такое окно мы увидим после выбора шаблона:
Вводим имя для реквеста. В итоге будет создан вот такой файл:
Только посмотрите сколько работы за меня делают шаблоны!
Пара моментов:
- У нас есть правило для именования реквестов — постфикс Request в имени. При создании файла мне не нужно об этом заботиться, я только ввожу имя реквеста, и по шаблону будет создан класс с правильным названием и именем файла.
GetMenuRequest
иGetMenuRequest.swift
в данном случае. - Класс имплементирует протокол, но большинство параметров — дефолтные. После создания файла, шаблон мне «подсказывает» места, которые скорее всего я хочу изменить.
Рассмотрим более интересное применение шаблонов — многофайловые шаблоны с возможностью кастомизации при создании (code variants).
В проекте «Додо Пиццы» мы отделяем вью от котроллера. Если это новый контроллер, то создаем под него сториборд/ксиб. Чтобы создать один экран, нужно создать минимум 3 файла: вью, контроллер и сториборд. Помимо этого, контроллер должен быть связан с вью, а сториборд иметь ссылки на вью и контроллер. При этом, мне не всегда нужно создавать файл сториборда (если он уже есть), или файл с вью (если она не нужна). В этой ситуации помогут многофайловые шаблоны с кастомизацией.
В нашем случае нужно будет создать 4 папки для шаблона:
- просто контроллер;
- контроллер и сториборд;
- контроллер и вью;
- контроллер, вью и сториборд;
Вот так будет выглядеть иерархия:
- ViewController.xctemplate
- — UIViewController
- — UIViewControllerStoryboard
- — UIViewControllerView
- — UIViewControllerViewStoryboard
- — TemplateIcon.png
- — TemplateIcon@2x.png
- — TemplateInfo.plist
Здесь уже привычные файлы с иконкой и конфигурацией, но сами шаблоны хранятся в папках. В зависимости от того, какие параметры мы укажем в Xcode при создании контроллера, выберется нужна папка.
Вот так может выглядеть plist:
http://www.apple.com/DTDs/PropertyList-1.0.dtd">
Kind
Xcode.IDEFoundation.TextSubstitutionFileTemplateKind
Description
ViewController class
Summary
ViewController class
SortOrder
1
DefaultCompletionName
ViewController
Platforms
com.apple.platform.iphoneos
Options
Identifier
viewControllerName
Required
Name
ViewController:
Description
The name of the view controller class to create
Type
text
NotPersisted
Identifier
cocoaTouchSubclass
Required
YES
Name
Subclass of:
Description
What class to subclass in the new file
Type
class
Default
UIViewController
NotPersisted
Identifier
View
Name
Also create View file
Description
Whether to create a View file with the same name
Type
checkbox
Default
true
NotPersisted
Identifier
Storyboard
Name
Also create Storyboard file
Description
Whether to create a Storyboard file with the same name
Type
checkbox
Default
true
NotPersisted
Default
___VARIABLE_viewControllerName___
Identifier
productName
Type
static
А вот что получим в итоге:
При такой конфигурации создаться три файла, связанные между собой. В них прописан весь минимально необходимый для работы код: в контроллере есть ссылка на вью, в сториборде есть ссылка на контроллер и вью.
Сила шаблонов в том, что на уровне создания файла можно конфигурировать, что именно нужно создать. Например, можно создать шаблон для VIPER-модуля и при создании указывать только то, что вам нужно (презентер с интерактором или весь модуль целиком).
Бонус секция для шаблонов
Как раньше упоминал, для шаблонов нет официальной документации и инструментов для их создания. Возможности шаблонов «перебираются» энтузиастами на основе стандартных шаблонов Xcode. Но есть одна фича, описание которой я не видел ни в одной статье по шаблонам (возможно, эта будет первой).
Фича в том, что в шаблоне мы можем сконфигурировать текстовое поле так, что оно будет выступать в роли превью для вводимого текста. Например, это можно видеть при создании нового проекта — поле Bundle Identifier
меняется в зависимости от поля Product Name
. В моем примере это выглядит так:
Потрясающе, ведь теперь я знаю, что мне не нужно дописывать бойлерплейт части ViewController
и View
.
Сделать это просто: в .plist
добавим новое текстовое поле, где в default-параметре укажем переменную для интересующего идентификатора.
// Поле ввода имени ViewController
Default
Massive
Identifier
productName
Required
Name
ViewController:
Description
The name of the view controller class to create
Type
text
NotPersisted
YES
// Превью имени ViewController
NotPersisted
Yes
Default ___VARIABLE_productName:identifier___ViewController
Description
ViewController
Identifier
previewViewController
Name
ViewController:
Type
static
Выглядит эффектно. Спасибо @ihppie за то, что рассказал про эту фичу.
Итог
С помощью простых и стандартных вещей можно избежать рутины и значительно сэкономить не только свое время, но и время команды. Все рассмотренные приемы позволяют фокусироваться на коде, а не взаимодействии с IDE во время его написания.
- Тренируйте шорткаты — работа на тачпаде/мышкой будет замедлять вас. Многие операции мышкой и так требуют зажатия клавиш, почему бы полностью не перебраться на клавиатуру?
- Используйте сниппеты для часто повторяемых, неделимых конструкций, project-specific кейсов.
- Создавайте шаблоны и управляйте не только создаваемым файлом, но и сопутствующими (code variants).
Код со всеми примерами в репозитории на GitHub.
Делитесь в комментариях своими джедайскими техниками работы с кодом, что используете и в каких количествах, как учили или учите — вместе мы станем на один шаг ближе к 10x инженеру. Чтобы не пропустить следующую статью, подписывайтесь на канал Dodo Pizza Mobile и Dodo Engineering.