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

в 9:14, , рубрики: iOS, swift, xcode, Блог компании Тинькофф Банк, разработка под 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 установлен, в конце каждой сборки проекта будет происходить проверка кода. После первого запуска вы вероятнее всего увидите что-то типа:

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

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

swiftlint autocorrect

Собираем проект заново: количество ошибок сократилось в разы, но оставшиеся придется исправлять вручную.

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

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

swiftlint rules

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

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

  • 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 — чистота и порядок в iOS проекте - 4

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

// 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+Stringss*."
    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

Автор: Тинькофф Банк

Источник

Поделиться

* - обязательные к заполнению поля