SwiftLint — чистота и порядок в iOS проекте
Думаю, все знают, как бывает непросто поддерживать соблюдение code style и соглашений в iOS-проекте. Сегодня поговорим о том, как автоматизировать этот процесс с помощью утилиты SwiftLint.
SwiftLint — это утилита от разработчиков Realm для автоматической проверки Swift-кода. Утилита содержит набор правил, основанных на GitHub’s Swift Style Guide и здравом смысле. Разумеется можно добавлять свои правила. SwiftLint поддерживает интеграцию с Xcode, Appcode, Atom.
Установка и настройка
Скачиваем и устанавливаем самый свежий релиз из репозитория.
Или устанавливаем через терминал:
$ brew update
$ brew install swiftlint
Обновить утилиту можно так:
$ brew update
$ brew upgrade swiftlint
Переключиться на другую версию:
$ brew switch swiftlint <номер версии>
Узнать текущую версию:
swiftlint version
Заходим в настройки проекта, Build Phases и добавляем в секцию Run Script:
if which swiftlint >/dev/null; then
swiftlint
else
echo "error: SwiftLint does not exist, download it from https://github.com/realm/SwiftLint"
exit 1
fi
Совет: крайне рекомендуется использовать команду «exit 1» — это гарантирует установку SwiftLint всеми членами команды.
Первый запуск
Теперь, когда SwiftLint установлен, в конце каждой сборки проекта будет происходить проверка кода. После первого запуска вы вероятнее всего увидите что-то типа:
Не стоит сильно переживать, большинство ошибок и предупреждений достаточно просты и легко исправимы. Не спешите исправлять их вручную, поскольку SwiftLint имеет замечательную функцию автокоррекции, она достаточно безопасна. Для её использования в терминале заходим в каталог проекта и выполняем команду:
swiftlint autocorrect
Собираем проект заново: количество ошибок сократилось в разы, но оставшиеся придется исправлять вручную.
Совет: не добавляйте автокоррекцию в Run Script, иначе программисты привыкнут не думать или даже не будут знать о некоторых соглашениях.
Правила
Список предустановленных правил можно увидеть, выполнив команду:
swiftlint rules
У каждого правила есть описание и набор параметров:
- opt-in — является ли правило опциональным (отключено по умолчанию);
- correctable — возможно ли настраивать правило;
- enabled in your config — включено ли в проекте;
- configuration — параметры.
Также можно посмотреть исходники правил для большего понимания.
Отдельно хочется выделить несколько очень полезных правил:
- сyclomatic_сomplexity — правило ограничивающее цикломатическую сложность;
- nesting — уровень вложенности классов и функций;
- file_lenght — количество строк в файле;
- function_body_lenght — количество строк в функции;
- force_try/cast/unwrapping — наличие операций, потенциально приводящих к крэшу;
- weak_delegate — проверка того, что делегат держится слабой ссылкой;
- missing_docs — написаны ли комментарии к публичным функциям/свойствам.
Конфигурация
Вероятнее всего вам потребуется отключить, настроить или добавить какие-либо правила. Для этого в корне проекта нужно создать файл .swiftlint.yml.
Доступные параметры конфигурации:
Список правил, которые необходимо отключить:
disabled_rules:
- colon
- comma
- control_statement
Список опциональных правил, которые необходимо включить (по умолчанию отключены):
opt_in_rules:
- empty_count
- missing_docs
Исключить подкаталоги или файлы:
excluded:
- Carthage
- Pods
- Source/ExcludedFolder
- Source/ExcludedFile.swift
Включить подкаталоги или файлы (альтернатива excluded):
included:
- MyProject
- MyProjectKeyboard
- MyProjectTests
Параметры правил (доступные параметры можно найти в списке правил):
file_length:
warning: 500
error: 600
Тип отчета (доступные параметры: xcode, json, csv, checkstyle, junit):
reporter: xcode
Максимально допустимое количество предупреждений:
warning_threshold: 15
Совет: обязательно добавьте в файл конфигурации warning_threshold, чтобы количество предупреждений не увеличивалось.
Вложенные конфигурации
Вы можете создавать несколько файлов конфигураций (.swiftlint.yml) для различных подкаталогов. SwiftLint будет автоматически использовать конфигурацию расположенную в папке с проверяемыми файлами. Параметры excluded и included для вложенных конфигураций будут игнорироваться.
Добавление своих правил
SwiftLint позволяет добавлять свои правила на основе регулярных выражений. Для этого необходимо в файле .swiftlint.yml добавить секцию custom_rules. У правила можно указать следующие параметры:
- identifer — идентификатор (обязательно);
- regexp — условие нахождения (обязательно);
- name — имя правила, отображается в тексте ошибки (необязательно);
- message — описание ошибки (обязательно);
- match_kinds — в каких сущностях искать вхождение, доступные значения (можно указывать несколько): comment, identifier, number, parameter, string, полный список доступен в документации (обязательно);
- severity — тип: предупреждение (warning) или ошибка (error) (необязательно, по умолчанию — warning);
- included — какие файлы необходимо проверять (необязательно
Правило, проверяющее именование с суффиксом -id. (например: userId — верно, userID — неверно):
custom_rules:
id_suffix_naming:
name: "Wrong name"
regex: "(ID)"
match_kinds:
- comment
- identifier
message: "Use 'Id' instead 'ID'"
severity: error
Отключение правил в коде
Если хотите отключить проверку в части кода, используйте следующую конструкцию:
// swiftlint:disable [ ...]
.........
// swiftlint:enable [ ...]
func printName() {
// swiftlint:disable force_cast
let name = loadName() as! String
// swiftlint:enable force_cast
print(name)
}
Скорость сборки проекта
Очевидно, что использование SwiftLint требует дополнительного времени при сборке проекта. Если скорость сборки заметно проседает, стоит задуматься о проверке только измененных файлов. Для этого нужно заменить скрипт проверки в Build Phases на:
count=0
for file_path in $(git ls-files -om --exclude-from=.gitignore | grep ".swift$"); do
export SCRIPT_INPUT_FILE_$count=$file_path
count=$((count + 1))
done
for file_path in $(git diff --cached --name-only | grep ".swift$"); do
export SCRIPT_INPUT_FILE_$count=$file_path
count=$((count + 1))
done
export SCRIPT_INPUT_FILE_COUNT=$count
swiftlint lint --use-script-input-files
Несколько примеров конфигураций SwiftLint в известных компаниях:
disabled_rules:
- function_parameter_count
- nesting
- variable_name
- weak_delegate
- trailing_comma
opt_in_rules:
- empty_count
- force_unwrapping
- private_outlet
line_length: 110
type_body_length:
warning: 300
error: 400
excluded:
- Carthage/
- Frameworks/
- Kickstarter-iOS.playground/
- Kickstarter-tvOS.playground/
- Library/Strings.swift
- bin/strings.swift
reporter: "xcode"
custom_rules:
localized_lensing:
name: "Localized Lensing"
regex: "\.~\s+Strings\s*\."
message: "Capture calls to `Strings` functions using `%~ { _ in Strings... }`"
severity: error
disabled_rules: # rule identifiers to exclude from running
- legacy_constructor
- variable_name
- legacy_cggeometry_functions
- legacy_constant
- todo
- trailing_newline
- empty_count
- force_cast
- type_name
- function_body_length
- missing_docs
- conditional_binding_cascade
- valid_docs
- cyclomatic_complexity
- type_body_length
- function_parameter_count
- force_try
- control_statement
- trailing_whitespace
- leading_whitespace
- operator_whitespace
- file_length
- mark
opt_in_rules: # some rules are only opt-in
- closing_brace
- opening_brace
- return_arrow_whitespace
- trailing_semicolon
- statement_position
# Find all the available rules by running:
# swiftlint rules
included: # paths to include during linting. `--path` is ignored if present.
excluded: # paths to ignore during linting. Takes precedence over `included`.
- Carthage
- Pods
- Source/ExcludedFolder
- Source/ExcludedFile.swift
- ThirdParty
- FxA
- FxAClient
- build
# configurable rules can be customized from this configuration file
# binary rules can set their severity level
trailing_semicolon: error
empty_count: error
closing_brace: error
opening_brace: error
return_arrow_whitespace: error
statement_position: error
colon: error
comma: error
line_length: 1000
reporter: "json" # reporter type (xcode, json, csv, checkstyle)
included:
- Realm/ObjectServerTests
- RealmSwift
- Realm/Swift
variable_name:
min_length: # not possible to disable this partial rule, so set it to zero
warning: 0
error: 0
disabled_rules:
- file_length
- force_cast
- force_try
- function_body_length
- todo
- type_body_length
- line_length
- vertical_whitespace
- syntactic_sugar
excluded:
— Pods
— MBUITest
disabled_rules:
— trailing_whitespace
line_length:
warning: 150
function_parameter_count:
warning: 10
error: 15
file_length:
warning: 500
type_body_length:
warning: 400
error: 450
SwiftLint позволил нашей команде ios разработчиков тратить меньше времени на код-ревью, придерживаться единого code style и повысить качество кода. Если вы еще не используете SwiftLint в своем проекте, то обязательно попробуйте.
Полезные ссылки:
» Репозиторий SwiftLint
» Список правил SwiftLint
» Про проверку только измененных файлов
» GitHub’s Swift Style Guide
» Tailor — альтернатива SwiftLint