
Go
I think everyone knows how difficult it is to maintain compliance with the code style and agreements in an iOS project. Today we will talk about how to automate this process using SwiftLint utility.
SwiftLint is a utility from Realm developers for automatically checking Swift-code. The utility contains a set of rules based on the
GitHub's Swift Style Guide and common sense. Of course, you can add your own rules. SwiftLint supports integration with Xcode, Appcode, Atom.
Installation and SetupDownload and install the latest release from the
repository .
')
Or install through the terminal:
$ brew update $ brew install swiftlint
You can update the utility like this:
$ brew update $ brew upgrade swiftlint
Switch to another version:
$ brew switch swiftlint < >
Check current version:
swiftlint version
Go to the project settings, Build Phases and add to the Run Script section:
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
Tip: it is highly recommended to use the “exit 1” command - this guarantees that all members of the team will install SwiftLint
First startNow that SwiftLint is installed, code verification will occur at the end of each build of the project. After the first launch, you will most likely see something like:

Do not worry much, most errors and warnings are quite simple and easily fixable. Do not rush to fix them manually, since SwiftLint has a great auto-correction feature, it is safe enough. To use it in the terminal, go to the project directory and execute the command:
swiftlint autocorrect
We collect the project again: the number of errors has been reduced by several times, but the rest will have to be corrected manually.
Tip: do not add auto-correction in Run Script, otherwise programmers will not get used to thinking or will not even be aware of some conventions.
rulesThe list of predefined rules can be seen by running the command:
swiftlint rules

Each rule has a description and a set of parameters:
- opt-in - whether the rule is optional (disabled by default);
- correctable - is it possible to customize the rule;
- enabled in your config - is it enabled in the project;
- configuration - parameters.
You can also see the
source of the rules for more understanding.
Separately, I want to highlight a few very useful rules:
- cyclomatic_complexity - the rule limiting cyclomatic complexity;
- nesting - the level of nesting of classes and functions;
- file_length - the number of lines in the file;
- function_body_length - the number of lines in the function;
- force_try / cast / unwrapping - the presence of operations that potentially lead to crash;
- weak_delegate - check that the delegate is holding a weak reference;
- missing_docs - whether comments on public functions / properties are written.
ConfigurationMost likely you will need to disable, configure or add any rules. To do this, you need to create a .swiftlint.yml file in the project root.
Available configuration options:
List of rules that need to be disabled:
disabled_rules: - colon - comma - control_statement
The list of optional rules that need to be enabled (disabled by default):
opt_in_rules: - empty_count - missing_docs
Exclude subdirectories or files:
excluded: - Carthage - Pods - Source/ExcludedFolder - Source/ExcludedFile.swift
Include subdirectories or files (alternatively excluded):
included: - MyProject - MyProjectKeyboard - MyProjectTests
Rule parameters (available parameters can be found in the list of rules):
file_length: warning: 500 error: 600
Report type (available options: xcode, json, csv, checkstyle, junit):
reporter: xcode
Maximum number of warnings allowed:
warning_threshold: 15
Tip: be sure to add warning_threshold to the configuration file so that the number of warnings does not increase.
Nested configurationsYou can create multiple configuration files (.swiftlint.yml) for various subdirectories. SwiftLint will automatically use the configuration located in the folder with the scanned files. Parameters excluded and included for nested configurations will be ignored.
Add your own rulesSwiftLint allows you to add your own rules based on regular expressions. To do this, in the .swiftlint.yml file, add the custom_rules section. You can specify the following parameters for the rule:
- identifer - identifier (required);
- regexp - condition of finding (required);
- name - the name of the rule, displayed in the text of the error (optional);
- message - error description (required);
- match_kinds - in which entities to search for an entry, available values (you can specify several): comment, identifier, number, parameter, string, the full list is available in the documentation (required);
- severity - type: warning (warning) or error (error) (optional, by default - warning);
- included - what files need to be checked (optional
Rule checking naming with suffix -id. (for example: userId is correct, userID is incorrect):
custom_rules: id_suffix_naming: name: "Wrong name" regex: "(ID)" match_kinds: - comment - identifier message: "Use 'Id' instead 'ID'" severity: error
Disable rules in codeIf you want to disable checking in part of the code, use the following construction:
// 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) }
Project build speedObviously, using SwiftLint requires additional time when building a project. If the build speed noticeably sags, you should consider checking only the modified files. To do this, replace the test script in Build Phases with:
Script 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
A few examples of SwiftLint configurations in well-known companies: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
Tinkoffwarning_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 allowed our ios development team to spend less time on code review, sticking to a single code style and improving code quality. If you are not using SwiftLint in your project, then be sure to try.
Useful links:»
SwiftLint repository»
SwiftLint rules list»
About checking only changed files»
GitHub's Swift Style Guide»
Tailor - an alternative to SwiftLint