Swiftlint
--- name: swiftlint emoji: "\U0001F9F9" requires: swiftlint install: brew install swiftlint description: Swift linting and style enforcement via CLI --- # SwiftLint Enforce Swift style and conventio
Description
name: swiftlint emoji: "\U0001F9F9" requires: swiftlint install: brew install swiftlint description: Swift linting and style enforcement via CLI
SwiftLint
Enforce Swift style and conventions with static analysis. Lint entire projects, autocorrect fixable violations, manage rules, and integrate with Xcode and CI — all from the CLI.
Verify Installation
swiftlint version
If not installed:
brew install swiftlint
Or via Mint:
mint install realm/SwiftLint
Or as a Swift Package Manager plugin (add to Package.swift):
.package(url: "https://github.com/realm/SwiftLint.git", from: "0.57.0")
Then run:
swift package plugin swiftlint
Basic Usage
Lint Current Directory
swiftlint
This recursively lints all .swift files from the current directory.
Lint a Specific Path
swiftlint lint --path Sources/
Lint Specific Files
swiftlint lint --path Sources/App/ViewModel.swift
Lint from Standard Input
cat MyFile.swift | swiftlint lint --use-stdin --quiet
Agent guidance: When a user says "check my code style" or "lint my Swift code," run
swiftlintfrom the project root. If they point to a specific file or folder, use--path.
Autocorrect
SwiftLint can automatically fix certain violations.
Fix All Autocorrectable Violations
swiftlint --fix
Fix a Specific Path
swiftlint --fix --path Sources/
Fix a Specific File
swiftlint --fix --path Sources/App/ViewModel.swift
Preview What Would Be Fixed (Dry Run)
Lint first to see violations, then fix:
swiftlint lint --path Sources/ && swiftlint --fix --path Sources/
Agent guidance: Always lint before autocorrecting so the user sees what will change. Some violations are not autocorrectable — report those separately after fixing.
Output Formats
Default (Human-Readable)
swiftlint
Output: Sources/App.swift:12:1: warning: Line Length Violation: ...
JSON
swiftlint lint --reporter json
CSV
swiftlint lint --reporter csv
Checkstyle (XML)
swiftlint lint --reporter checkstyle
GitHub Actions
swiftlint lint --reporter github-actions-logging
Xcode Summary (plist)
swiftlint lint --reporter xcode-summary
SonarQube
swiftlint lint --reporter sonarqube
Markdown
swiftlint lint --reporter markdown
Save to File
swiftlint lint --reporter json > swiftlint-results.json
All Available Reporters
| Reporter | Format | Best For |
|---|---|---|
xcode |
Xcode-compatible (default) | Local development |
json |
JSON array | Programmatic processing |
csv |
CSV | Spreadsheet analysis |
checkstyle |
XML | Jenkins, CI tools |
codeclimate |
Code Climate JSON | Code Climate integration |
github-actions-logging |
GitHub annotations | GitHub Actions CI |
sonarqube |
SonarQube JSON | SonarQube integration |
markdown |
Markdown table | PR comments |
emoji |
Emoji-decorated | Fun terminal output |
html |
HTML report | Browser viewing |
junit |
JUnit XML | Test reporting tools |
xcode-summary |
Plist | Xcode build summaries |
Agent guidance: Use
--reporter jsonwhen you need to parse results programmatically. Use--reporter github-actions-loggingon GitHub Actions to get inline annotations on PRs.
Rules
List All Rules
swiftlint rules
Search for a Specific Rule
swiftlint rules | grep "force_cast"
Show Rule Details
swiftlint rules force_cast
Rule Identifiers Quick Reference
Rules fall into categories:
Enabled by Default (Common)
| Rule | What It Catches |
|---|---|
line_length |
Lines exceeding max length (default 120 warning, 200 error) |
trailing_whitespace |
Whitespace at end of lines |
trailing_newline |
Missing or extra trailing newlines |
opening_brace |
Opening brace placement |
closing_brace |
Closing brace placement |
colon |
Colon spacing (e.g., let x : Int → let x: Int) |
comma |
Comma spacing |
force_cast |
Use of as! |
force_try |
Use of try! |
force_unwrapping |
Use of ! on optionals (opt-in) |
type_body_length |
Type bodies exceeding max lines |
function_body_length |
Function bodies exceeding max lines |
file_length |
Files exceeding max lines |
cyclomatic_complexity |
High cyclomatic complexity |
nesting |
Deep nesting levels |
identifier_name |
Naming convention violations |
type_name |
Type naming convention violations |
unused_import |
Unused import statements (opt-in) |
unused_declaration |
Unused declarations (opt-in) |
vertical_whitespace |
Excessive blank lines |
todo |
TODO/FIXME comments as warnings |
mark |
Improper MARK comment format |
void_return |
Explicit -> Void instead of -> () |
syntactic_sugar |
Prefer [Int] over Array<Int> |
redundant_optional_initialization |
var x: Int? = nil (nil is default) |
redundant_string_enum_value |
Enum case name matches raw value |
Opt-In (Must Be Explicitly Enabled)
| Rule | What It Catches |
|---|---|
explicit_type_interface |
Missing explicit type annotations |
missing_docs |
Missing documentation comments |
multiline_arguments |
Multiline function call formatting |
multiline_parameters |
Multiline function param formatting |
vertical_parameter_alignment |
Parameter alignment in declarations |
sorted_imports |
Unsorted import statements |
file_header |
Missing or incorrect file headers |
accessibility_label_for_image |
Images without accessibility labels |
accessibility_trait_for_button |
Buttons without accessibility traits |
strict_fileprivate |
Prefer private over fileprivate |
prohibited_interface_builder |
Storyboard/XIB usage |
no_magic_numbers |
Magic numbers in code |
prefer_self_in_static_references |
Self over explicit type name in static context |
balanced_xctest_lifecycle |
setUp without tearDown |
test_case_accessibility |
Test methods not marked correctly |
Agent guidance: When setting up SwiftLint for a new project, start with defaults and only add opt-in rules the user specifically requests. Don't overwhelm with every possible rule.
Configuration (.swiftlint.yml)
SwiftLint reads .swiftlint.yml from the current directory (or parent directories).
Generate a Default Config
There's no built-in generator, but here's a minimal config:
# .swiftlint.yml
# Paths to include (default: all Swift files)
included:
- Sources
- Tests
# Paths to exclude
excluded:
- Pods
- DerivedData
- .build
- Packages
# Disable specific rules
disabled_rules:
- todo
- trailing_whitespace
# Enable opt-in rules
opt_in_rules:
- sorted_imports
- unused_import
- missing_docs
# Configure specific rules
line_length:
warning: 120
error: 200
ignores_comments: true
ignores_urls: true
type_body_length:
warning: 300
error: 500
file_length:
warning: 500
error: 1000
function_body_length:
warning: 50
error: 100
identifier_name:
min_length:
warning: 2
error: 1
max_length:
warning: 50
error: 60
excluded:
- id
- x
- y
- i
- j
- ok
type_name:
min_length:
warning: 3
error: 0
max_length:
warning: 50
error: 60
cyclomatic_complexity:
warning: 10
error: 20
nesting:
type_level:
warning: 2
function_level:
warning: 3
Using a Config from a Custom Path
swiftlint lint --config path/to/.swiftlint.yml
Multiple Configs (Child Config)
# Feature/.swiftlint.yml — inherits parent config and overrides
child_config: ../.swiftlint.yml
disabled_rules:
- force_cast
Parent Config (Extend from Another)
# .swiftlint.yml
parent_config: shared/.swiftlint-base.yml
Remote Config
parent_config: https://raw.githubusercontent.com/org/repo/main/.swiftlint.yml
Agent guidance: When a project already has
.swiftlint.yml, always read it before suggesting changes. Modify the existing config rather than creating a new one.
Inline Rule Management
Disable a Rule for a Line
let value = dict["key"] as! String // swiftlint:disable:this force_cast
Disable a Rule for a Block
// swiftlint:disable force_cast
let a = x as! String
let b = y as! Int
// swiftlint:enable force_cast
Disable All Rules for a Block
// swiftlint:disable all
// Legacy code that can't be refactored yet
// swiftlint:disable:enable all
Disable for Next Line Only
// swiftlint:disable:next force_cast
let value = dict["key"] as! String
Disable for Previous Line
let value = dict["key"] as! String
// swiftlint:disable:previous force_cast
Agent guidance: Prefer targeted disables (
disable:this,disable:next) over broad block disables. Never suggestdisable allunless the user is dealing with generated code or legacy code they explicitly don't want to lint.
Xcode Integration
Build Phase Script
Add a Run Script Phase in Xcode (Build Phases):
if command -v swiftlint >/dev/null 2>&1; then
swiftlint
else
echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
fi
Build Phase with Autocorrect
if command -v swiftlint >/dev/null 2>&1; then
swiftlint --fix && swiftlint
fi
Swift Package Plugin (Xcode 15+)
If SwiftLint is added as a package dependency:
swift package plugin swiftlint
Or in Xcode: right-click the project → "SwiftLintBuildToolPlugin".
Agent guidance: For modern projects (Xcode 15+), prefer the SPM plugin over a build phase script — it pins the SwiftLint version to the project and doesn't require a local install.
CI Integration
GitHub Actions
- name: SwiftLint
run: |
brew install swiftlint
swiftlint lint --reporter github-actions-logging --strict
With only changed files:
- name: SwiftLint Changed Files
run: |
git diff --name-only --diff-filter=d origin/main...HEAD -- '*.swift' | \
xargs -I{} swiftlint lint --path {} --reporter github-actions-logging --strict
Bitrise
- script:
inputs:
- content: |
brew install swiftlint
swiftlint lint --strict
Jenkins (Checkstyle)
swiftlint lint --reporter checkstyle > swiftlint-checkstyle.xml
Then use the Checkstyle plugin to parse swiftlint-checkstyle.xml.
Analyzing Results
Count Violations by Rule
swiftlint lint --reporter json | python3 -c "
import json, sys, collections
data = json.load(sys.stdin)
counts = collections.Counter(v['rule_id'] for v in data)
for rule, count in counts.most_common():
print(f'{count:>5} {rule}')
"
Show Only Errors (Not Warnings)
swiftlint lint --strict 2>&1 | grep "error:"
Strict Mode (Warnings Become Errors)
swiftlint lint --strict
This makes SwiftLint return a non-zero exit code for any violation (not just errors).
Quiet Mode (Errors Only in Output)
swiftlint lint --quiet
Suppresses warnings from output, only shows errors.
Enable/Disable Specific Rules via CLI
swiftlint lint --enable-rules sorted_imports,unused_import
swiftlint lint --disable-rules todo,trailing_whitespace
Custom Rules
Define custom regex-based rules in .swiftlint.yml:
custom_rules:
no_print_statements:
name: "No Print Statements"
regex: "\\bprint\\s*\\("
message: "Use os_log or Logger instead of print()"
severity: warning
match_kinds:
- identifier
no_hardcoded_strings:
name: "No Hardcoded Strings in Views"
regex: "Text\\(\"[^\"]+\"\\)"
message: "Use LocalizedStringKey or String(localized:) for user-facing text"
severity: warning
included: ".*View\\.swift"
no_force_unwrap_iboutlet:
name: "No Force Unwrap IBOutlet"
regex: "@IBOutlet\\s+(weak\\s+)?var\\s+\\w+:\\s+\\w+!"
message: "Use optional IBOutlets to avoid crashes"
severity: error
accessibility_identifier_required:
name: "Accessibility Identifier"
regex: "\\.accessibilityIdentifier\\("
message: "Good — accessibilityIdentifier found"
severity: warning
match_kinds:
- identifier
prefer_logger_over_print:
name: "Prefer Logger"
regex: "\\bNSLog\\s*\\("
message: "Use Logger (os.Logger) instead of NSLog"
severity: warning
Match Kinds
| Kind | What It Matches |
|---|---|
identifier |
Variable/function names |
string |
String literals |
comment |
Comments |
keyword |
Swift keywords |
typeidentifier |
Type names |
number |
Numeric literals |
parameter |
Function parameters |
argument |
Function arguments |
Agent guidance: Custom rules are powerful but regex-based — they can produce false positives. Always test custom rules on the codebase before suggesting them as permanent additions.
Common Flags Reference
| Flag | Purpose |
|---|---|
--path <path> |
Lint specific file or directory |
--config <path> |
Use custom config file |
--reporter <name> |
Output format (json, csv, etc.) |
--strict |
Treat warnings as errors |
--quiet |
Only show errors in output |
--fix |
Autocorrect fixable violations |
--enable-rules <rules> |
Enable specific rules (comma-separated) |
--disable-rules <rules> |
Disable specific rules (comma-separated) |
--use-stdin |
Read Swift from stdin |
--force-exclude |
Exclude files even if explicitly passed |
--cache-path <path> |
Custom cache directory |
--no-cache |
Disable caching |
--use-alternative-excluding |
Use alternative file-excluding method |
--in-process-sourcekit |
Use in-process SourceKit |
--compiler-log-path |
Path to xcodebuild log for analyzer rules |
--progress |
Show progress bar |
Common Workflows
Set Up SwiftLint for a New Project
# 1. Install
brew install swiftlint
# 2. Run initial lint to see baseline
swiftlint lint --path Sources/ --reporter json > baseline.json
# 3. Create config based on results
cat > .swiftlint.yml << 'EOF'
included:
- Sources
- Tests
excluded:
- Pods
- DerivedData
- .build
disabled_rules:
- todo
opt_in_rules:
- sorted_imports
- unused_import
line_length:
warning: 120
error: 200
ignores_urls: true
EOF
# 4. Fix autocorrectable issues
swiftlint --fix --path Sources/
# 5. Re-lint to see remaining issues
swiftlint
Fix All Issues Before a PR
# Autocorrect what we can
swiftlint --fix
# Check what remains
swiftlint lint --strict
Lint Only Changed Files (vs. main)
git diff --name-only --diff-filter=d origin/main...HEAD -- '*.swift' | \
xargs -I{} swiftlint lint --path {} --strict
Compare Violation Counts Between Branches
# Current branch count
CURRENT=$(swiftlint lint --quiet 2>&1 | wc -l | tr -d ' ')
# Main branch count
git stash
git checkout main
MAIN=$(swiftlint lint --quiet 2>&1 | wc -l | tr -d ' ')
git checkout -
git stash pop
echo "main: $MAIN violations, current: $CURRENT violations"
Troubleshooting
Common Errors
| Error | Solution |
|---|---|
No lintable files found |
Check included paths in config or run from project root |
Invalid configuration |
Validate YAML syntax in .swiftlint.yml |
SourceKit not found |
Run sudo xcode-select -s /Applications/Xcode.app |
Rule not found |
Check rule name with swiftlint rules, may be opt-in |
Slow linting |
Add excluded paths for Pods/DerivedData, use --cache-path |
Performance Tips
- Always exclude
Pods/,DerivedData/,.build/, andPackages/in config - Use
--cache-pathto persist cache across CI runs - Lint only changed files on CI instead of the entire project
- Use
--no-cacheonly when debugging unexpected results
Notes
Agent Tips
When a user asks to "clean up" or "fix style" in Swift code, run
swiftlint --fixfirst, thenswiftlint lintto report remaining issues.
Before adding SwiftLint to an existing project, run
swiftlint lint --reporter jsonto assess the violation count. If there are hundreds of violations, suggest a phased approach: fix autocorrectable ones first, then tackle the rest by category.
Always check for an existing
.swiftlint.ymlbefore creating one. Read it to understand the team's style preferences.
For projects targeting accessibility (which should be all of them), suggest enabling
accessibility_label_for_imageandaccessibility_trait_for_buttonopt-in rules.
When the user's project uses SwiftUI, suggest a custom rule to catch hardcoded strings in
Text()views — all user-facing strings should useString(localized:)for localization support.
The
--strictflag is essential for CI — without it, SwiftLint exits 0 even with warnings, which means your CI pipeline won't catch style regressions.
Reviews (0)
No reviews yet. Be the first to review!
Comments (0)
No comments yet. Be the first to share your thoughts!