SwiftLint — чистота и порядок в iOS проекте

image

Думаю, все знают, как бывает непросто поддерживать соблюдение 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 установлен, в конце каждой сборки проекта будет происходить проверка кода. После первого запуска вы вероятнее всего увидите что-то типа:

082bd5ccd6cf4b36ae58cd74ddeb73c0.jpg

Не стоит сильно переживать, большинство ошибок и предупреждений достаточно просты и легко исправимы. Не спешите исправлять их вручную, поскольку SwiftLint имеет замечательную функцию автокоррекции, она достаточно безопасна. Для её использования в терминале заходим в каталог проекта и выполняем команду:

swiftlint autocorrect

Собираем проект заново: количество ошибок сократилось в разы, но оставшиеся придется исправлять вручную.
Совет: не добавляйте автокоррекцию в Run Script, иначе программисты привыкнут не думать или даже не будут знать о некоторых соглашениях.

Правила
Список предустановленных правил можно увидеть, выполнив команду:
swiftlint rules

df7cf34ad12c40fb938cd151acac3cfa.png

У каждого правила есть описание и набор параметров:

  • 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

81a1acbdfc784ef0827c08f0fe1d867e.png

Отключение правил в коде
Если хотите отключить проверку в части кода, используйте следующую конструкцию:

// 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 в известных компаниях:
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


SwiftLint позволил нашей команде ios разработчиков тратить меньше времени на код-ревью, придерживаться единого code style и повысить качество кода. Если вы еще не используете SwiftLint в своем проекте, то обязательно попробуйте.

Полезные ссылки:

» Репозиторий SwiftLint
» Список правил SwiftLint
» Про проверку только измененных файлов
» GitHub’s Swift Style Guide
» Tailor — альтернатива SwiftLint

Комментарии (0)

© Habrahabr.ru