Думаю, все знают, как бывает непросто поддерживать соблюдение 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_length — количество строк в файле;
- function_body_length — количество строк в функции;
- 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 <rule1> [<rule2> <rule3>...] ......... // swiftlint:enable <rule1> [<rule2> <rule3>...] 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 в известных компаниях:
Kickstarter
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
Firefox
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)
Realm
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
Тинькофф
warning_threshold: 15
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
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
