
Swiftlint
- 1.6k installs
- 944 repo stars
- Updated July 15, 2026
- dpearson2699/swift-ios-skills
swiftlint is an agent skill for configures and enforces swiftlint in swift projects using build tool plugins, run scripts, and ci. covers .swiftlint.yml configuration, disabled_rules, opt_in_rules,.
About
The swiftlint skill is designed for configures and enforces SwiftLint in Swift projects using build tool plugins, run scripts, and CI. Covers .swiftlint.yml configuration, disabled_rules, opt_in_rules,. SwiftLint SwiftLint enforces Swift style and conventions by linting source files against a configurable rule set. This skill covers setup, configuration, rule selection, suppression, CI integration, and rollout strategy. Invoke when the user setting up SwiftLint, configuring lint rules, suppressing warnings, creating baselines, choosing between build tool plugin and run script, or integrating SwiftLint into CI.
- Recommended Setup.
- Configuration.
- Rule Selection Strategy.
- CI Integration.
- Integration Decision Tree.
Swiftlint by the numbers
- 1,582 all-time installs (skills.sh)
- +118 installs in the week ending Jul 29, 2026 (Skillselion tracking)
- Ranked #152 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
swiftlint capabilities & compatibility
- Capabilities
- recommended setup · configuration · rule selection strategy · ci integration
- Use cases
- frontend
What swiftlint says it does
Configures and enforces SwiftLint in Swift projects using build tool plugins, run scripts, and CI. Covers .swiftlint.yml configuration, disabled_rules, opt_in_rules, only_rules, an
Configures and enforces SwiftLint in Swift projects using build tool plugins, run scripts, and CI. Covers .swiftlint.yml configuration, disabled_rules, opt_in_r
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill swiftlintAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.6k |
|---|---|
| repo stars | ★ 944 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 15, 2026 |
| Repository | dpearson2699/swift-ios-skills ↗ |
How do I configures and enforces swiftlint in swift projects using build tool plugins, run scripts, and ci. covers .swiftlint.yml configuration, disabled_rules, opt_in_rules,?
Configures and enforces SwiftLint in Swift projects using build tool plugins, run scripts, and CI. Covers .swiftlint.yml configuration, disabled_rules, opt_in_rules,.
Who is it for?
Developers using swiftlint workflows documented in SKILL.md.
Skip if: Skip when the task falls outside swiftlint scope or needs a different stack.
When should I use this skill?
User setting up SwiftLint, configuring lint rules, suppressing warnings, creating baselines, choosing between build tool plugin and run script, or integrating SwiftLint into CI.
What you get
Completed swiftlint workflow with documented commands, files, and expected deliverables.
- .swiftlint.yml configuration
- Build tool plugin or CI lint step
- Baseline file for legacy violations
By the numbers
- 3 SwiftLint rule categories: default, opt-in, and analyzer rules
- 5 bundled reference documents for configuration, plugins, rules, and custom rules
- 8 common mistakes documented including build-phase --fix and only_rules misuse
Files
SwiftLint
SwiftLint enforces Swift style and conventions by linting source files against a configurable rule set. This skill covers setup, configuration, rule selection, suppression, CI integration, and rollout strategy.
SwiftLint is a style enforcement tool, not a style guide. For underlying Swift naming and design conventions, see swift-api-design-guidelines. For architecture patterns, see swift-architecture.
Contents
- Recommended Setup
- Configuration
- Rule Selection Strategy
- Suppressions
- Baselines
- Autocorrect
- CI Integration
- Integration Decision Tree
- Multiple Configurations
- Common Mistakes
- Review Checklist
- References
---
Recommended Setup
Default: build tool plugin via `SimplyDanny/SwiftLintPlugins`.
Add the plugin package to Package.swift or via Xcode's package dependencies:
// Package.swift
dependencies: [
.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "<reviewed-version>")
]For SwiftPM targets, apply the plugin:
.target(
name: "MyApp",
plugins: [.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins")]
)For Xcode projects without a Package.swift, add the package dependency in the project settings, then enable the plugin under the target's Build Phases or the package's plugin trust dialog.
The build tool plugin runs SwiftLint automatically on every build. No run script required.
First build: Xcode prompts to trust the plugin. Select "Trust & Enable All" for the SwiftLintPlugins package.
For alternatives (run scripts, command plugin, Homebrew CLI), see references/plugins-run-scripts-and-integrations.md.
Configuration
Create .swiftlint.yml at the project root. SwiftLint loads the main configuration from the invocation or plugin working directory, then can merge the nearest nested .swiftlint.yml for each file when configs are discovered automatically. Passing --config overrides automatic discovery and disables nested-config lookup.
# .swiftlint.yml — conservative starter config
disabled_rules:
- trailing_whitespace
- todo
opt_in_rules:
- empty_count
- closure_spacing
- force_unwrapping
- sorted_imports
- vertical_whitespace_opening_braces
- private_swiftui_state
- unhandled_throwing_task
- accessibility_label_for_image
included:
- Sources
- Tests
excluded:
- .build
- DerivedData
- "**/.build"
- "**/Generated"
line_length:
warning: 140
error: 200
type_body_length:
warning: 300
error: 500
file_length:
warning: 500
error: 1000Key configuration options:
| Key | Purpose |
|---|---|
disabled_rules | Turn off default-enabled rules |
opt_in_rules | Turn on rules not enabled by default |
only_rules | Use _only_ the listed rules (mutually exclusive with disabled_rules/opt_in_rules) |
analyzer_rules | Rules requiring compiler logs (run via swiftlint analyze) |
baseline | Path to an existing baseline file used to suppress known violations |
write_baseline | Path where SwiftLint should write a new baseline file |
included | Paths to lint (default: current directory) |
excluded | Paths to skip |
strict | Elevate all warnings to errors |
lenient | Downgrade all errors to warnings |
allow_zero_lintable_files | Suppress the error when no Swift files are found |
reporter | Output format: xcode (default), json, checkstyle, sarif, csv, emoji, etc. |
For full configuration details including severity tuning, environment-variable interpolation, and nested/remote configs, see references/adoption-and-configuration.md.
Rule Selection Strategy
SwiftLint ships with three rule categories:
1. Default rules — enabled automatically, cover widely accepted conventions 2. Opt-in rules — disabled by default, enable selectively via opt_in_rules 3. Analyzer rules — require compiler logs, enabled via analyzer_rules
Browse the full categorized list at <https://realm.github.io/SwiftLint/rule-directory.html>.
Recommended approach for new projects:
1. Start with defaults. Run swiftlint rules to see which rules are enabled. 2. Disable rules that conflict with your team's established conventions. 3. Add opt-in rules one at a time. Review violations before committing each addition. 4. Do not use only_rules unless you have a specific reason to start from zero.
Recommended approach for existing codebases:
1. Start with the default rule set. 2. Create a baseline (see Baselines) to suppress all existing violations. 3. Enforce zero new violations in CI. 4. Burn down baseline violations incrementally.
Do not transcribe or memorize the rule directory. Look up rule identifiers and configuration options at the official rule directory when needed.
Suppressions
Suppress SwiftLint for specific lines when a rule produces a false positive or when the violation is intentional and reviewed.
// swiftlint:disable:next force_cast
let view = object as! UIView
let legacy = try! JSONDecoder().decode(T.self, from: data) // swiftlint:disable:this force_try
// swiftlint:disable:previous large_tupleDisable for a region:
// swiftlint:disable cyclomatic_complexity
func complexRouter(...) { ... }
// swiftlint:enable cyclomatic_complexityDisable all rules (use sparingly):
// swiftlint:disable all
// ... generated or legacy code ...
// swiftlint:enable allPolicy:
- Prefer targeted single-rule suppressions over
all. - Always re-enable after the region ends.
- For generated code, prefer
excludedpaths in.swiftlint.ymlover inline suppressions. - For test targets with different tolerance, use a child configuration (see Multiple Configurations).
For full suppression syntax, see references/rules-suppressions-and-baselines.md.
Baselines
Baselines let you adopt SwiftLint in an existing codebase without fixing every legacy violation first.
Create a baseline:
swiftlint --write-baseline .swiftlint.baselineThis records all current violations. Future runs compare against this baseline and only report new violations.
Use the baseline:
swiftlint --baseline .swiftlint.baselineIn CI, pass --baseline so only new violations fail the build. Burn down the baseline over time by fixing legacy violations and regenerating.
For baseline workflows and rollout strategy, see references/rules-suppressions-and-baselines.md.
Autocorrect
SwiftLint can fix some violations automatically:
swiftlint --fix
# or the legacy alias:
swiftlint --autocorrectWarnings:
- Never run `--fix` as a pre-compile build phase. Auto-fixes modify source files. If run automatically on every build, this creates an unpredictable edit-build loop and can mask real issues.
- Run
--fixmanually or in a dedicated CI step, then review the diff. - Not all rules support autocorrect. Check
swiftlint rules— the "Correctable" column shows which rules can auto-fix. - Always commit or stash before running
--fix.
CI Integration
CI is the primary enforcement surface. A CI check ensures no one merges code that increases the violation count.
Recommended CI pattern:
# GitHub Actions example
- name: Lint
run: |
brew install swiftlint
swiftlint --strict --reporter sarif > swiftlint.sarifKey CI options:
| Flag | Effect |
|---|---|
--strict | Exits non-zero on warnings (not just errors) |
--reporter sarif | GitHub Advanced Security compatible output |
--reporter json | Machine-readable output |
--reporter checkstyle | Jenkins/SonarQube compatible |
--baseline .swiftlint.baseline | Only fail on new violations |
For SARIF upload to GitHub code scanning, add github/codeql-action/upload-sarif after the lint step.
For full CI recipes and reporter details, see references/plugins-run-scripts-and-integrations.md.
Integration Decision Tree
Choose how to run SwiftLint based on project shape:
| Scenario | Recommended integration |
|---|---|
SwiftPM package or Xcode project with Package.swift | Build tool plugin via SwiftLintPlugins |
SwiftPM project needing CLI flags (--fix, --baseline) | Command plugin: swift package plugin swiftlint |
| Xcode project without SwiftPM, team uses Homebrew | Run script build phase |
| CI/CD pipeline | Homebrew or Docker install, run swiftlint directly |
| Pre-commit hook | Homebrew install + .pre-commit-config.yaml or git hook script |
The build tool plugin is preferred for local development because it requires no PATH configuration, pins the SwiftLint version via package resolution, and runs automatically on build.
For detailed setup instructions for each integration, see references/plugins-run-scripts-and-integrations.md.
Multiple Configurations
SwiftLint supports layered configuration files. A .swiftlint.yml in a subdirectory inherits from and overrides the parent config.
Common patterns:
- Relaxed test config: place a
.swiftlint.ymlinTests/that disablesforce_unwrappingand raisesfile_length - Strict module config: place a stricter
.swiftlint.ymlin a shared module directory - Remote config: use
parent_configwith an HTTPS URL to pull a shared team config (caching supported)
# Tests/.swiftlint.yml — child config
disabled_rules:
- force_unwrapping
- force_try
file_length:
warning: 800You can also pass multiple configs on the CLI:
swiftlint --config .swiftlint.yml --config .swiftlint-extra.ymlLater configs override earlier ones for overlapping keys.
For nested config resolution, remote configs, and CLI multi-config details, see references/adoption-and-configuration.md.
Common Mistakes
1. Running `--fix` in a build phase. Auto-fixing on every build creates unpredictable source modifications. Run --fix manually.
2. Using `only_rules` without understanding the implication. This disables all rules except those listed. Most teams should use disabled_rules + opt_in_rules instead.
3. Suppressing with `// swiftlint:disable all` and forgetting to re-enable. This silently disables all linting for the rest of the file.
4. Not pinning the SwiftLint version. Different versions have different default rules. Use the build tool plugin (version pinned via SPM) or pin in your Brewfile / CI config.
5. Excluding too broadly. Excluding Tests/ entirely means test code gets no linting. Use a child config with relaxed rules instead.
6. Ignoring the toolchain mismatch. SwiftLint must be built with (or compatible with) the same Swift toolchain used to compile your project. Mismatches cause parsing errors. See references/plugins-run-scripts-and-integrations.md for multi-toolchain guidance.
7. Adopting too many opt-in rules at once in a large codebase. This creates an overwhelming number of violations. Add rules incrementally and use baselines.
8. Not configuring `included` paths. Without included, SwiftLint scans the working directory recursively, which may pick up vendored or generated code.
Review Checklist
- [ ]
.swiftlint.ymlexists at the project root with explicitincluded/excludedpaths - [ ] SwiftLint version is pinned (via SPM plugin resolution, Brewfile, or CI config)
- [ ] Build tool plugin is enabled for each target that should be linted
- [ ] CI runs
swiftlint --strict(or with--baselinefor incremental adoption) - [ ] No
--fix/--autocorrectin build phases - [ ] Inline suppressions target specific rules, not
all - [ ] Inline suppressions include a comment explaining why
- [ ] Test targets have appropriate config (relaxed rules via child config, not excluded entirely)
- [ ] Autocorrect changes are reviewed in a separate commit
- [ ] New opt-in rules are added one at a time with team consensus
References
- references/adoption-and-configuration.md — Installation paths,
.swiftlint.ymldeep dive, severity tuning, environment variables, nested/remote configs, rollout strategy - references/plugins-run-scripts-and-integrations.md — Build tool plugin, command plugin, run scripts, CI recipes, multi-toolchain guidance, VS Code, Fastlane, Docker, pre-commit
- references/rules-suppressions-and-baselines.md — Default vs opt-in vs analyzer rules, suppression syntax, baseline workflows, false-positive handling
- references/rule-reference.md — Bundled exhaustive rule index for local lookup; verify current details with
swiftlint rulesor the official rule directory - references/custom-rules-and-analyze.md — Regex custom rules, Swift custom rules (brief),
swiftlint analyze, compiler-log workflow - SwiftLint documentation — Official docs
- SwiftLint rule directory — Full categorized rule list
- SimplyDanny/SwiftLintPlugins — Recommended plugin package
{
"skill_name": "swiftlint",
"evals": [
{
"id": 0,
"name": "plugin-setup-ci",
"prompt": "Set up SwiftLint for a new iOS app that has a SwiftPM `Package.swift` and Xcode targets. The team wants local linting on builds, a pinned SwiftLint version, GitHub Actions output that can be uploaded as SARIF, and guidance for unattended CI builds. Give a concise setup plan with the key snippets and warnings.",
"expected_output": "A SwiftLint setup plan that defaults to the SwiftLintPlugins build tool plugin, pins the package version, applies the plugin per target, notes Xcode plugin trust and unattended validation risk, and uses a direct SwiftLint CLI/SARIF step in CI.",
"files": [],
"assertions": [
"Uses `https://github.com/SimplyDanny/SwiftLintPlugins` and `SwiftLintBuildToolPlugin` as the default local integration.",
"Says the plugin dependency should be pinned or reviewed rather than floating implicitly.",
"Applies the build tool plugin to each target that should be linted.",
"Mentions the Xcode trust prompt and warns that `-skipPackagePluginValidation` / `-skipMacroValidation` bypass validation and should be limited to trusted CI dependencies.",
"Uses a direct CI command such as `swiftlint --strict --reporter sarif > swiftlint.sarif` plus SARIF upload rather than trying to use the build tool plugin for CI-only reporting."
]
},
{
"id": 1,
"name": "baseline-suppression-rollout",
"prompt": "We are adopting SwiftLint in a large existing Swift app with thousands of current violations. Propose a rollout plan that lets CI fail only on new violations, explains how to handle intentional exceptions in code, and says how test code should be treated. Include the relevant `.swiftlint.yml` or command snippets.",
"expected_output": "An incremental adoption plan using baselines, strict CI with baseline filtering, targeted inline suppressions with reasons, generated-code exclusions, and child test configuration rather than excluding tests entirely.",
"files": [],
"assertions": [
"Creates a baseline with `swiftlint --write-baseline` or `write_baseline` and commits it for incremental adoption.",
"Runs CI with `--baseline` and `--strict` or equivalent config so only new violations fail.",
"Recommends targeted `swiftlint:disable` suppressions with reasons instead of blanket `all` suppressions.",
"Prefers config-level `excluded` paths for generated or vendored code.",
"Recommends a relaxed child `.swiftlint.yml` for tests instead of excluding the whole test suite."
]
},
{
"id": 2,
"name": "sibling-boundary-routing",
"prompt": "I need help with three cleanup tasks in a Swift package: choose better public API names for `func fetchDataWithId(_ id: String)`, decide whether `some` or `any` is right for a return type, and configure SwiftLint to enforce identifier naming and sorted imports. Which skill should own each part? Keep the answer short and give only minimal examples.",
"expected_output": "A boundary-aware routing answer that sends API naming to swift-api-design-guidelines, type-system `some`/`any` guidance to swift-language, and lint rule configuration to swiftlint with a minimal configuration snippet.",
"files": [],
"assertions": [
"Routes public API naming and argument-label clarity to `swift-api-design-guidelines`.",
"Routes `some` vs `any` protocol type-system guidance to `swift-language`.",
"Routes SwiftLint identifier naming and sorted imports configuration to `swiftlint`.",
"Provides only a minimal SwiftLint snippet, such as `identifier_name` and `opt_in_rules: sorted_imports`, without turning the answer into a full lint setup guide.",
"Does not collapse Swift language or API design guidance into the SwiftLint skill."
]
}
]
}
Adoption and Configuration
Detailed guidance on installing SwiftLint, configuring .swiftlint.yml, and rolling out linting in existing codebases.
Contents
- Installation Paths
- Configuration File Discovery
- Configuration Deep Dive
- Severity Tuning
- Environment Variable Interpolation
- Nested and Child Configurations
- Remote Configuration
- Rollout Strategy for Existing Codebases
---
Installation Paths
| Method | When to use |
|---|---|
| SwiftLintPlugins SPM package | Default for any project with Package.swift. Pins version automatically. |
Homebrew (brew install swiftlint) | CI runners, pre-commit hooks, standalone CLI usage. |
Mint (mint install realm/SwiftLint) | Teams using Mint for tool management. |
CocoaPods (pod 'SwiftLint') | Legacy projects already using CocoaPods. Binary is at ${PODS_ROOT}/SwiftLint/swiftlint. |
| Pre-built binary | Download from GitHub releases. Useful for controlled CI environments. |
The build tool plugin (via SwiftLintPlugins) is recommended over all other local integration methods. See the plugins and integrations reference linked from SKILL.md for setup details.
Configuration File Discovery
SwiftLint treats the top-level .swiftlint.yml as the main configuration, then optionally merges the nearest nested .swiftlint.yml found while walking up from an individual file.
- If no config is found, SwiftLint uses its built-in defaults.
- A project root config applies to all files unless a nested config refines it for a subtree.
- At most one nested
.swiftlint.ymlis merged for any given file. - Passing
--configoverrides automatic discovery entirely and disables nested-config lookup.
Working directory behavior:
- The build tool plugin uses the topmost
.swiftlint.ymlwithin the package/project directory as its working directory, and falls back to the package/project root if no config file is found there. - Run scripts use
${SRCROOT}or the Xcode build setting for the working directory. - CLI invocations use the shell's current directory.
Configuration Deep Dive
Rule control keys
# Enable defaults minus these:
disabled_rules:
- trailing_whitespace
- todo
# Add these on top of defaults:
opt_in_rules:
- empty_count
- closure_spacing
- sorted_imports
- vertical_whitespace_opening_braces
- contains_over_filter_count
- first_where
- last_where
- modifier_orderonly_rules is mutually exclusive with disabled_rules and opt_in_rules. Use it only when you want to start from an empty rule set and explicitly list every rule:
only_rules:
- line_length
- force_cast
- force_tryAnalyzer rules
Analyzer rules require passing compiler logs to SwiftLint. They are not included in default or opt-in sets:
analyzer_rules:
- unused_import
- unused_declarationSee the custom rules and analyze reference linked from SKILL.md.
Path control
included:
- Sources
- Tests
excluded:
- .build
- DerivedData
- Carthage
- Pods
- "**/Generated"
- "**/Snapshots"included and excluded support glob patterns. Paths are relative to the config file's directory.
Reporter
reporter: xcode # default — Xcode-compatible warnings/errors
# reporter: json
# reporter: sarif
# reporter: checkstyle
# reporter: csv
# reporter: emoji
# reporter: github-actions-loggingUse sarif for GitHub code scanning integration. Use json for custom tooling. Use github-actions-logging for inline PR annotations without SARIF upload.
Global modifiers
strict: true # all warnings become errors
# lenient: true # all errors become warnings (useful during initial adoption)
allow_zero_lintable_files: true # don't error when no .swift files are found (useful in CI)Severity Tuning
Most rules accept warning and error thresholds:
line_length:
warning: 140
error: 200
ignores_comments: true
ignores_urls: true
ignores_interpolated_strings: true
type_body_length:
warning: 300
error: 500
file_length:
warning: 500
error: 1000
ignore_comment_only_lines: true
function_body_length:
warning: 50
error: 100
cyclomatic_complexity:
warning: 10
error: 20
ignores_case_statements: true
nesting:
type_level: 2
function_level: 3
identifier_name:
min_length:
warning: 2
error: 1
max_length:
warning: 50
error: 60
excluded:
- id
- x
- y
- i
- j
- toCheck available configuration keys for any rule with swiftlint rules <rule_name> or the rule directory.
Environment Variable Interpolation
Configuration values can reference environment variables:
included:
- ${PROJECT_DIR}/Sources
excluded:
- ${PROJECT_DIR}/GeneratedThis is useful when SwiftLint is invoked from different working directories (e.g., run scripts vs CLI).
Nested and Child Configurations
SwiftLint supports both explicit parent/child config chaining and automatic nested configs.
Explicit local parent/child configs
Use child_config and parent_config to layer configs deliberately:
# .swiftlint.yml
child_config: .swiftlint-strict.yml
parent_config: Base/.swiftlint-base.ymlchild_config refines the current config with higher priority. parent_config provides lower-priority defaults.
Automatic nested configs
A .swiftlint.yml in a subdirectory can also act as a nested child config for files in that subtree:
MyProject/
├── .swiftlint.yml # root config
├── Sources/
│ └── .swiftlint.yml # stricter config for production code (optional)
└── Tests/
└── .swiftlint.yml # relaxed config for test codeExample child config for tests:
# Tests/.swiftlint.yml
disabled_rules:
- force_unwrapping
- force_try
- force_cast
file_length:
warning: 800
error: 1500
function_body_length:
warning: 100
error: 200Rule state from parent and child configs is merged. A child config only overrides the parent when it explicitly states the opposite for the same rule. For example, a parent disabled_rules entry still applies unless the child opt-ins that same rule, and a parent opt-in still applies unless the child disables it.
For included and excluded, SwiftLint applies special merge behavior: paths are resolved relative to each config file, child excluded entries can remove parent included entries, and child included entries can re-include paths excluded by the parent.
Nested configs are only used when SwiftLint discovers configs automatically. If you pass --config, nested discovery is disabled.
CLI multi-config
Pass multiple configs via CLI. Later configs override earlier ones:
swiftlint --config .swiftlint.yml --config .swiftlint-strict.ymlRemote Configuration
Pull a shared team configuration using parent_config with an HTTPS URL:
parent_config: https://example.com/team-swiftlint.yml
remote_timeout: 2 # seconds, default is 2
remote_timeout_if_cached: 1 # seconds, used when a cached version existsRemote configs are cached locally. If the fetch fails or times out, the cached version is used. If no cache exists and the fetch fails, SwiftLint fails with an error.
Caution: Remote configs introduce a network dependency. Ensure CI runners can reach the URL, or use a local copy as a fallback.
Rollout Strategy for Existing Codebases
Adopting SwiftLint in an existing project without disrupting the team:
Phase 1: Baseline
1. Install SwiftLint with defaults (or your team's starter config). 2. Run once to see the violation landscape: swiftlint --reporter json | python3 -m json.tool | head -50 3. Generate a baseline: swiftlint --write-baseline .swiftlint.baseline 4. Commit the baseline and config. CI now passes with --baseline .swiftlint.baseline.
Phase 2: Stop the bleeding
1. CI enforces swiftlint --strict --baseline .swiftlint.baseline — no new violations allowed. 2. The build tool plugin shows warnings locally (developers see issues as they edit). 3. Do not enable --fix in CI or build phases.
Phase 3: Incremental cleanup
1. Pick a rule with many baseline violations. Fix violations in a dedicated PR. 2. Regenerate the baseline after each cleanup PR. 3. Add opt-in rules one at a time once the team is comfortable. 4. Move toward removing the baseline entirely as violations are cleaned up.
Phase 4: Mature enforcement
1. Baseline is empty or removed. 2. --strict in CI, build tool plugin in local builds. 3. New opt-in rules go through team review before enabling. 4. Consider analyzer rules for high-value checks like unused_import.
Custom Rules and Analyze
Regex custom rules for project-specific enforcement, brief coverage of Swift custom rules, and the swiftlint analyze workflow.
Contents
---
Regex Custom Rules
Regex custom rules let you enforce project-specific patterns directly in .swiftlint.yml without building a custom SwiftLint binary.
custom_rules:
no_print_statements:
name: "No print()"
regex: '^\s*print\s*\('
message: "Use os_log or Logger instead of print()"
severity: warning
match_kinds:
- identifier
no_hardcoded_colors:
name: "No hardcoded colors"
regex: 'UIColor\(\s*red:|\.init\(\s*red:'
message: "Use Color asset catalog entries instead of hardcoded RGB values"
severity: warning
todo_requires_ticket:
name: "TODO requires ticket"
regex: '//\s*TODO(?!.*\b[A-Z]+-\d+)'
message: "TODOs must reference a ticket (e.g., TODO: PROJ-123)"
severity: warningCustom rule configuration keys
| Key | Required | Description |
|---|---|---|
name | No | Human-readable name shown in violations |
regex | Yes | The pattern to match |
capture_group | No | Which regex capture group to highlight; defaults to 0 (the whole match) |
message | No | Custom violation message |
severity | No | warning (default) or error |
match_kinds | No | Limit matches to specific syntax kinds (e.g., comment, identifier, string) |
excluded_match_kinds | No | Exclude specific syntax kinds from matching; cannot be combined with match_kinds |
included | No | Regex pattern for file paths to include (note: regex, not glob) |
excluded | No | Regex pattern for file paths to exclude (note: regex, not glob) |
execution_mode | No | Per-rule execution mode: default, swiftsyntax, or sourcekit |
match_kinds values
Syntax token kinds that SwiftLint recognizes: argument, attribute.builtin, attribute.id, buildconfig.id, buildconfig.keyword, comment, comment.mark, comment.url, doccomment, doccomment.field, identifier, keyword, number, objectliteral, parameter, placeholder, string, string_interpolation_anchor, typeidentifier.
Use match_kinds to avoid false positives. For example, matching print only in identifier context avoids flagging it inside strings or comments.
Regex flags: Custom rule regexes run with s (dot matches newlines) and m (^/$ match line boundaries) enabled by default. Prepend (?-s) if you don't want . to match newlines.
`only_rules` interaction: If using only_rules alongside custom_rules, you must include the literal string custom_rules in your only_rules list, or custom rules will not run.
Execution mode: Individual custom rules can set execution_mode to default, swiftsyntax, or sourcekit. You can also set a top-level default_execution_mode to apply the same mode across all custom regex rules unless a rule overrides it.
default_execution_mode: swiftsyntax
custom_rules:
no_print_statements:
regex: '^\s*print\s*\('
execution_mode: sourcekitSwift Custom Rules
SwiftLint supports rules written in Swift using the SwiftSyntax AST. These are more powerful than regex rules but require building SwiftLint from source with Bazel.
This is an advanced workflow primarily useful for organizations that need precise AST-based enforcement. For most projects, regex custom rules are sufficient.
If you need Swift custom rules:
1. Clone realm/SwiftLint 2. Scaffold a new rule with swift run swiftlint-dev rules template <RuleName> or add it under Source/SwiftLintBuiltInRules/Rules/ 3. Register the rule so it becomes part of the executable (make register in the SwiftLint repo workflow) 4. Build the binary with Bazel: bazel build :swiftlint 5. Use the resulting custom swiftlint binary in your project
This is out of scope for typical project-level adoption.
SwiftLint Analyze
swiftlint analyze runs analyzer rules that require the Swift compiler's type-checked AST. These rules can detect issues like unused imports and unused declarations that are impossible to catch with syntactic analysis alone.
Workflow
1. Perform a clean build and capture the compiler log (incremental builds will fail):
# Xcode — clean build required
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp clean build \
| tee xcodebuild.log
# SwiftPM — clean build required, -v needed for compiler command lines
swift package clean
swift build -v 2>&1 | tee swift-build.log1. Run analyze with the compiler log:
swiftlint analyze --compiler-log-path xcodebuild.logOr via the command plugin:
swift package plugin swiftlint -- analyze --compiler-log-path swift-build.log1. Configure which analyzer rules to enable:
# .swiftlint.yml
analyzer_rules:
- unused_import
- unused_declarationAutocorrect with analyze
Analyzer rules that support autocorrect can fix issues:
swiftlint analyze --fix --compiler-log-path xcodebuild.logunused_import is the most commonly used correctable analyzer rule — it removes unnecessary import statements.
When to Use Analyzer Rules
Analyzer rules are slower than regular rules because they require a full build first. Use them when:
- Codebase hygiene matters:
unused_importcatches import bloat that accumulates over refactoring. - Dead code detection:
unused_declarationfinds private declarations that are never referenced. - CI only: Run analyzer rules in CI rather than on every local build to avoid slowing down the development loop.
A practical pattern is to run analyzer rules in a separate CI job that runs less frequently (e.g., nightly or on main branch only):
# GitHub Actions — nightly analyze
on:
schedule:
- cron: '0 6 * * *'
jobs:
analyze:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: swift build -v 2>&1 | tee swift-build.log
- name: Analyze
run: |
brew install swiftlint
swiftlint analyze --strict --compiler-log-path swift-build.logPlugins, Run Scripts, and Integrations
Setup instructions for each SwiftLint integration method: build tool plugin, command plugin, Xcode run scripts, CI, and secondary integrations.
Contents
- Build Tool Plugin (Recommended)
- Command Plugin
- Xcode Run Script Build Phase
- CI Recipes
- Working With Multiple Swift Versions
- VS Code
- Fastlane
- Docker
- Pre-commit Hook
---
Build Tool Plugin (Recommended)
The SwiftLintBuildToolPlugin from SimplyDanny/SwiftLintPlugins runs SwiftLint as part of the build. No Homebrew or PATH setup needed.
SwiftPM setup
// Package.swift
let package = Package(
name: "MyApp",
dependencies: [
.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "<reviewed-version>")
],
targets: [
.target(
name: "MyApp",
plugins: [.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins")]
),
.testTarget(
name: "MyAppTests",
dependencies: ["MyApp"],
plugins: [.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins")]
)
]
)Xcode project setup (no Package.swift)
1. File > Add Package Dependencies → add https://github.com/SimplyDanny/SwiftLintPlugins 2. For each target you want to lint, go to Build Phases and add SwiftLintBuildToolPlugin under Run Build Tool Plug-ins 3. When prompted, trust the plugin
Plugin trust
On first build, Xcode shows a trust dialog. Select Trust & Enable All for the SwiftLintPlugins package. In CI with xcodebuild, pass:
xcodebuild -skipPackagePluginValidation -skipMacroValidation ...These unattended flags bypass Xcode's validation dialogs and implicitly trust package plugins and macros. Use them only for reviewed dependencies in controlled CI.
Limitations
- The build tool plugin cannot run
--fix(it has read-only access to sources). - It cannot pass
--baselineor other CLI flags — build tool plugins do not accept arguments. Use config keys likebaseline:/write_baseline:where available, or switch to the command plugin / direct CLI for advanced flag-based workflows. - It may fail when Swift files or the config live outside the package/project directory because it cannot pass
--config. Add a local.swiftlint.ymlwithparent_config:pointing to the shared config, or use a run script. - It runs on every build, which is desirable for local development but may slow clean builds in large projects.
Command Plugin
The command plugin provides broad SwiftPM-based CLI access to SwiftLint, including --fix, --baseline, and analyze workflows that the build tool plugin cannot handle directly:
swift package plugin swiftlint
swift package plugin swiftlint --fix
swift package plugin swiftlint -- --strict --baseline .swiftlint.baseline
swift package plugin swiftlint -- analyze --compiler-log-path swift-build.logThe command plugin requires the same SwiftLintPlugins dependency. It accepts SwiftLint CLI flags after --; when using --fix, expect SwiftPM's package-directory write-permission handling because fixes can modify source files.
Xcode Run Script Build Phase
Use a run script when the build tool plugin is impractical for your project shape or when you need CLI features the build tool plugin cannot provide (for example --fix locally or --baseline). Xcode projects can still use the build tool plugin via Xcode Package Dependency even without a local Package.swift.
Basic run script
1. Select the target → Build Phases → + → New Run Script Phase 2. Move the phase after Compile Sources — SwiftLint is designed to analyze valid, compilable source code; linting before compilation leads to confusing results 3. Add the script:
if command -v swiftlint >/dev/null 2>&1; then
swiftlint
else
echo "warning: SwiftLint not installed. Install with: brew install swiftlint"
fiOn Apple Silicon with Homebrew, swiftlint is often installed at /opt/homebrew/bin/swiftlint. If the run script cannot find it, either export that path in the build phase or create a symlink into /usr/local/bin:
if [[ "$(uname -m)" == arm64 ]]; then
export PATH="/opt/homebrew/bin:$PATH"
fi
if command -v swiftlint >/dev/null 2>&1; then
swiftlint
else
echo "warning: SwiftLint not installed. Install with: brew install swiftlint"
fiRun script with script input files (Xcode 15+)
Xcode 15 sandboxes run scripts by default. If SwiftLint fails with Sandbox: swiftlint ... deny(1) file-read-data, set ENABLE_USER_SCRIPT_SANDBOXING = NO for the target. Input files and input file lists are a separate optimization for limiting which files are linted.
1. Set ENABLE_USER_SCRIPT_SANDBOXING = NO for the target if the script cannot read source files under Xcode 15+ 2. Under the run script phase, check Based on dependency analysis 3. Add either explicit Input Files or readable .xcfilelist paths under Input File Lists 4. Use --use-script-input-file-lists when Xcode is providing .xcfilelist paths:
if command -v swiftlint >/dev/null 2>&1; then
swiftlint --use-script-input-file-lists
fiThis requires Xcode to populate SCRIPT_INPUT_FILE_LIST_COUNT and SCRIPT_INPUT_FILE_LIST_n with readable .xcfilelist paths. Use --use-script-input-files only when Xcode is populating SCRIPT_INPUT_FILE_COUNT and SCRIPT_INPUT_FILE_n directly via Input Files rather than Input File Lists.
Alternatively, to lint the full target without file lists:
if command -v swiftlint >/dev/null 2>&1; then
swiftlint lint --config "${SRCROOT}/.swiftlint.yml"
fiAnd uncheck Based on dependency analysis if you want it to run every build.
CocoaPods run script
"${PODS_ROOT}/SwiftLint/swiftlint"CI Recipes
GitHub Actions
name: SwiftLint
on:
pull_request:
paths: ['**/*.swift']
jobs:
lint:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Install SwiftLint
run: brew install swiftlint
- name: Lint
run: swiftlint --strict --reporter github-actions-loggingFor SARIF upload to GitHub code scanning:
- name: Lint (SARIF)
run: swiftlint --strict --reporter sarif > swiftlint.sarif
continue-on-error: true
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: swiftlint.sarifGitHub Actions with baseline
- name: Lint (baseline)
run: swiftlint --strict --baseline .swiftlint.baseline --reporter github-actions-loggingGitLab CI
swiftlint:
image: ghcr.io/realm/swiftlint:latest
script:
- swiftlint --strict --reporter codeclimate > swiftlint.json
artifacts:
reports:
codequality: swiftlint.jsonIf you want GitLab JUnit-style output instead, use --reporter gitlab and publish the result as a JUnit artifact rather than codequality.
Bitrise / other CI
brew install swiftlint
swiftlint --strictReporter summary
| Reporter | Format | Best for |
|---|---|---|
xcode | Xcode-compatible text | Local builds, Xcode run scripts |
github-actions-logging | GitHub Actions annotations | PR inline comments |
sarif | SARIF JSON | GitHub code scanning |
json | JSON array | Custom tooling, dashboards |
checkstyle | XML | Jenkins, SonarQube |
csv | CSV | Spreadsheet analysis |
emoji | Text with emoji | Fun terminal output |
Working With Multiple Swift Versions
SwiftLint is predominantly SwiftSyntax-based, but some rules still rely on SourceKit/Clang for additional analysis. It must remain compatible with the Swift toolchain used to compile your project.
Key rules:
1. Run SwiftLint with the same Swift toolchain used to build your project. 2. On macOS, SwiftLint resolves the toolchain in this order: XCODE_DEFAULT_TOOLCHAIN_OVERRIDE, TOOLCHAIN_DIR or TOOLCHAINS, xcrun -find swift, /Applications/Xcode.app/..., /Applications/Xcode-beta.app/..., ~/Applications/Xcode.app/..., ~/Applications/Xcode-beta.app/.... 3. In CI with multiple Xcode versions, set DEVELOPER_DIR before running SwiftLint:
export DEVELOPER_DIR=/Applications/Xcode_16.app/Contents/Developer
swiftlint1. The build tool plugin automatically uses the correct toolchain because it runs within the build system. 2. Homebrew-installed SwiftLint may lag behind the latest Swift release. If you see parsing errors after updating Xcode, check for a SwiftLint update. 3. sourcekitd.framework is expected in the selected toolchain’s usr/lib/ directory. Toolchain mismatches typically show up as parsing or SourceKit failures.
VS Code
The SwiftLint VS Code extension runs SwiftLint on save.
// .vscode/settings.json
{
"swiftlint.enable": true,
"swiftlint.path": "/opt/homebrew/bin/swiftlint",
"swiftlint.autoLintWorkspace": false
}Fastlane
# Fastfile
lane :lint do
swiftlint(
mode: :lint,
config_file: ".swiftlint.yml",
strict: true,
raise_if_swiftlint_error: true
)
endDocker
The official SwiftLint Docker image is useful for Linux CI:
docker run --rm -v "$(pwd):/work" -w /work ghcr.io/realm/swiftlint:<reviewed-version> --strictPre-commit Hook
Using the pre-commit framework
# .pre-commit-config.yaml
repos:
- repo: https://github.com/realm/SwiftLint
rev: <reviewed-version>
hooks:
- id: swiftlintTo apply fixes and fail on warnings/errors from the hook, use an entry override:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/realm/SwiftLint
rev: <reviewed-version>
hooks:
- id: swiftlint
entry: swiftlint --fix --strictManual git hook
#!/bin/sh
# .git/hooks/pre-commit
if command -v swiftlint >/dev/null 2>&1; then
git diff --cached --name-only --diff-filter=d -- '*.swift' | \
xargs -I{} swiftlint lint --path "{}" --strict --quiet
fiMake it executable: chmod +x .git/hooks/pre-commit
Bundled Rule Index
This file is an exhaustive local index of SwiftLint rule identifiers grouped by the official rule-directory categories. Use it to avoid leaving a rule out when working offline or scanning the repo quickly.
Treat the official rule directory and swiftlint rules <identifier> as the source of truth for current descriptions, configuration keys, autocorrect support, and future rule additions or reclassifications.
Contents
---
Default Rules
These rules are enabled automatically. Disable specific ones via disabled_rules in .swiftlint.yml.
attribute_name_spacing, blanket_disable_command, block_based_kvo, class_delegate_protocol, closing_brace, closure_parameter_position, colon, comma, comment_spacing, compiler_protocol_init, computed_accessors_order, control_statement, custom_rules, cyclomatic_complexity, deployment_target, discouraged_direct_init, duplicate_conditions, duplicate_enum_cases, duplicate_imports, duplicated_key_in_dictionary_literal, dynamic_inline, empty_enum_arguments, empty_parameters, empty_parentheses_with_trailing_closure, file_length, for_where, force_cast, force_try, function_body_length, function_name_whitespace, function_parameter_count, generic_type_name, identifier_name, implicit_getter, implicit_optional_initialization, inclusive_language, invalid_swiftlint_command, invisible_character, is_disjoint, large_tuple, leading_whitespace, legacy_cggeometry_functions, legacy_constant, legacy_constructor, legacy_hashing, legacy_nsgeometry_functions, legacy_random, line_length, mark, multiple_closures_with_trailing_closure, nesting, no_fallthrough_only, no_space_in_method_call, non_optional_string_data_conversion, notification_center_detachment, ns_number_init_as_function_reference, nsobject_prefer_isequal, opening_brace, optional_data_string_conversion, orphaned_doc_comment, prefer_type_checking, private_over_fileprivate, private_unit_test, protocol_property_accessors_order, reduce_boolean, redundant_discardable_let, redundant_objc_attribute, redundant_sendable, redundant_set_access_control, redundant_string_enum_value, redundant_void_return, return_arrow_whitespace, self_in_property_initialization, shorthand_operator, statement_position, static_over_final_class, superfluous_disable_command, switch_case_alignment, syntactic_sugar, todo, trailing_comma, trailing_newline, trailing_semicolon, trailing_whitespace, type_body_length, type_name, unavailable_condition, unneeded_break_in_switch, unneeded_override, unneeded_synthesized_initializer, unused_closure_parameter, unused_control_flow_label, unused_enumerated, unused_optional_binding, unused_setter_value, valid_ibinspectable, vertical_parameter_alignment, vertical_whitespace, void_function_in_ternary, void_return, xctfail_message
---
Opt-in Rules
These rules are disabled by default. Enable selectively via opt_in_rules in .swiftlint.yml.
accessibility_label_for_image, accessibility_trait_for_button, anonymous_argument_in_multiline_closure, array_init, async_without_await, attributes, balanced_xctest_lifecycle, closure_body_length, closure_end_indentation, closure_spacing, collection_alignment, comma_inheritance, conditional_returns_on_newline, contains_over_filter_count, contains_over_filter_is_empty, contains_over_first_not_nil, contains_over_range_nil_comparison, contrasted_opening_brace, convenience_type, direct_return, discarded_notification_center_observer, discouraged_assert, discouraged_default_parameter, discouraged_none_name, discouraged_object_literal, discouraged_optional_boolean, discouraged_optional_collection, empty_collection_literal, empty_count, empty_string, empty_xctest_method, enum_case_associated_values_count, expiring_todo, explicit_acl, explicit_enum_raw_value, explicit_init, explicit_top_level_acl, explicit_type_interface, extension_access_modifier, fallthrough, fatal_error_message, file_header, file_name, file_name_no_space, file_types_order, final_test_case, first_where, flatmap_over_map_reduce, force_unwrapping, function_default_parameter_at_end, ibinspectable_in_extension, identical_operands, implicit_return, implicitly_unwrapped_optional, incompatible_concurrency_annotation, indentation_width, joined_default_parameter, last_where, legacy_multiple, legacy_objc_type, legacy_uigraphics_function, let_var_whitespace, literal_expression_end_indentation, local_doc_comment, lower_acl_than_parent, missing_docs, modifier_order, multiline_arguments, multiline_arguments_brackets, multiline_call_arguments, multiline_function_chains, multiline_literal_brackets, multiline_parameters, multiline_parameters_brackets, nimble_operator, no_empty_block, no_extension_access_modifier, no_grouping_extension, no_magic_numbers, non_overridable_class_declaration, nslocalizedstring_key, nslocalizedstring_require_bundle, number_separator, object_literal, one_declaration_per_file, operator_usage_whitespace, optional_enum_case_matching, overridden_super_call, override_in_extension, pattern_matching_keywords, period_spacing, prefer_asset_symbols, prefer_condition_list, prefer_key_path, prefer_nimble, prefer_self_in_static_references, prefer_self_type_over_type_of_self, prefer_zero_over_explicit_init, prefixed_toplevel_constant, private_action, private_outlet, private_subject, private_swiftui_state, prohibited_interface_builder, prohibited_super_call, quick_discouraged_call, quick_discouraged_focused_test, quick_discouraged_pending_test, raw_value_for_camel_cased_codable_enum, reduce_into, redundant_final, redundant_nil_coalescing, redundant_self, redundant_type_annotation, required_deinit, required_enum_case, return_value_from_void_function, self_binding, shorthand_argument, shorthand_optional_binding, single_test_class, sorted_enum_cases, sorted_first_last, sorted_imports, static_operator, strict_fileprivate, strong_iboutlet, superfluous_else, switch_case_on_newline, test_case_accessibility, toggle_bool, trailing_closure, type_contents_order, unavailable_function, unhandled_throwing_task, unneeded_escaping, unneeded_parentheses_in_closure_argument, unneeded_throws_rethrows, unowned_variable_capture, untyped_error_in_catch, unused_parameter, variable_shadowing, vertical_parameter_alignment_on_call, vertical_whitespace_between_cases, vertical_whitespace_closing_braces, vertical_whitespace_opening_braces, weak_delegate, xct_specific_matcher, yoda_condition
---
Analyzer Rules
These rules require the Swift compiler's type-checked AST. Run via swiftlint analyze --compiler-log-path <log>. See the custom rules and analyze reference for the full workflow.
capture_variable, explicit_self, typesafe_array_init, unused_declaration, unused_import
Rules, Suppressions, and Baselines
Guidance on SwiftLint rule categories, inline suppression syntax, baseline workflows, and false-positive handling.
Contents
- Rule Categories
- Browsing Rules
- Suppression Syntax
- Suppression Policy
- Baselines
- False Positives
- Generated Code and Test Targets
---
Rule Categories
SwiftLint rules fall into three categories:
Default rules
Enabled automatically. These cover widely agreed-upon conventions (e.g., line_length, force_cast, trailing_semicolon). Disable specific ones via disabled_rules in .swiftlint.yml.
Opt-in rules
Disabled by default because they are more opinionated or may not suit every project. Enable selectively via opt_in_rules:
opt_in_rules:
- empty_count
- closure_spacing
- force_unwrapping
- sorted_imports
- contains_over_filter_count
- first_where
- last_where
- modifier_order
- vertical_whitespace_opening_braces
- explicit_init
- joined_default_parameter
- redundant_nil_coalescing
- private_swiftui_state
- unhandled_throwing_task
- accessibility_label_for_image
- accessibility_trait_for_buttonAnalyzer rules
Require the Swift compiler's AST information. Must be run via swiftlint analyze with compiler logs. See the custom rules and analyze reference linked from SKILL.md.
analyzer_rules:
- unused_import
- unused_declarationBrowsing Rules
List all rules and their status:
swiftlint rules # all rules with enabled/disabled/correctable status
swiftlint rules --enabled # only currently enabled rules
swiftlint rules --disabled # only currently disabled rules
swiftlint rules <rule_identifier> # detailed info for one ruleThe official rule directory provides descriptions, configuration options, and examples for every rule.
Do not memorize or transcribe the rule directory. Look up specific rules when needed.
Commonly Encountered Rules Quick Reference
An agent writing or reviewing Swift code should understand what these frequently triggered rules enforce. Each rule can be configured in .swiftlint.yml using the rule identifier as a key.
Default rules (enabled automatically)
| Rule | What it enforces | Key config options |
|---|---|---|
line_length | Max characters per line | warning, error, ignores_urls, ignores_comments, ignores_interpolated_strings |
file_length | Max lines per file | warning, error, ignore_comment_only_lines |
type_body_length | Max lines in a type body | warning, error |
function_body_length | Max lines in a function body | warning, error |
function_parameter_count | Max parameters per function | warning, error, ignores_default_parameters |
cyclomatic_complexity | Max branching complexity | warning, error, ignores_case_statements |
nesting | Max nesting depth | type_level, function_level |
blanket_disable_command | Prevents swiftlint:disable from disabling rules for the rest of the file | allowed_rules, always_blanket_disable |
identifier_name | Naming length and conventions | min_length, max_length, excluded (list of allowed short names like id, x, i) |
type_name | Type naming length and conventions | min_length, max_length, excluded |
large_tuple | Max tuple size | warning, error |
force_cast | Flags as! | severity only |
force_try | Flags try! | severity only |
todo | Flags // TODO: and // FIXME: | severity only |
trailing_whitespace | Trailing spaces on lines | ignores_empty_lines, ignores_comments |
trailing_comma | Trailing commas in collections | mandatory_comma (when true, requires trailing commas) |
vertical_whitespace | Max consecutive blank lines | max_empty_lines |
opening_brace | Brace placement ({ on same line) | allow_multiline_func |
colon | Spacing around colons | flexible_right_spacing, apply_to_dictionaries |
deployment_target | Flags @available / #available checks that use versions already satisfied by the deployment target | iOS_deployment_target, macOS_deployment_target, etc. |
High-value opt-in rules
| Rule | What it enforces | Why enable it |
|---|---|---|
force_unwrapping | Flags ! unwraps | Catches crashes; relax in tests via child config |
private_swiftui_state | @State, @StateObject, @FocusState must be private | Prevents accidental external mutation of view state |
unhandled_throwing_task | Task { try ... } without do/catch | Silently swallowed errors in async contexts |
sorted_imports | Import statements in alphabetical order | Reduces merge conflicts; auto-correctable |
modifier_order | Consistent declaration modifier ordering | Readability; auto-correctable |
accessibility_label_for_image | Images must have accessibility labels | Accessibility compliance |
accessibility_trait_for_button | Buttons must have accessibility traits | Accessibility compliance |
empty_count | Use .isEmpty instead of .count == 0 | Performance and clarity |
closure_spacing | Spaces inside closure braces | Formatting consistency |
contains_over_filter_count | .contains instead of .filter { }.count | Performance |
first_where / last_where | .first(where:) instead of .filter { }.first | Performance |
redundant_nil_coalescing | Flags x ?? nil | Dead code |
implicit_return | Single-expression returns don't need return | Modern Swift style |
self_binding | Consistent guard let self naming | Configurable: bind_identifier |
shorthand_optional_binding | if let x instead of if let x = x | Swift 5.7+ style |
expiring_todo | TODOs/FIXMEs with dates become warnings or errors after expiry | Project hygiene with configurable thresholds and severities |
Analyzer rules (require compiler logs)
| Rule | What it enforces |
|---|---|
unused_import | Flags unnecessary import statements; auto-correctable |
unused_declaration | Flags private declarations never referenced |
capture_variable | Flags mutable variables captured by closures |
explicit_self | Requires self. for instance members |
typesafe_array_init | Flags Array(x.map { ... }) → use x.map { ... } directly |
Per-rule configuration pattern
Every rule that accepts configuration uses its identifier as the YAML key:
# Threshold-based rules use warning/error:
line_length:
warning: 140
error: 200
# Boolean option rules:
trailing_comma:
mandatory_comma: true
# Rules with excluded identifiers:
identifier_name:
excluded:
- id
- x
- y
# Rules with a single severity override:
force_cast: error # shorthand for severity: error
# Deployment target rule:
deployment_target:
iOS_deployment_target: "16.0"Run swiftlint rules <rule_identifier> to see all available configuration keys for any rule.
Suppression Syntax
Single-line suppressions
// swiftlint:disable:next force_cast
let view = object as! UIView
let value = dict["key"]! // swiftlint:disable:this force_unwrapping
// swiftlint:disable:previous large_tuple:next— suppresses on the next line:this— suppresses on the same line:previous— suppresses on the previous line
Region suppressions
// swiftlint:disable cyclomatic_complexity function_body_length
func complexLegacyFunction() {
// ... long function ...
}
// swiftlint:enable cyclomatic_complexity function_body_lengthMultiple rules can be listed in one directive, separated by spaces.
Disable all rules
// swiftlint:disable all
// ... entire block is unlinted ...
// swiftlint:enable allIf you forget // swiftlint:enable all, the rest of the file is unlinted. This is a common mistake.
Suppression Policy
- Target specific rules. Never use
// swiftlint:disable allunless the block is generated code that cannot be excluded via config. - Add a reason. Follow the suppression with a brief comment explaining why:
// swiftlint:disable:next force_cast — guaranteed by Interface Builder outlet type
let cell = tableView.dequeueReusableCell(...) as! CustomCell- Re-enable after regions. Always pair
disablewithenable. - Prefer config-level exclusion for entire files or directories of generated code.
- Review suppressions in code review. Inline suppressions should be as scrutinized as the code they protect.
- Treat suppressions as tech debt. Track and reduce them over time.
Baselines
Baselines record all existing violations so only new violations are reported.
Creating a baseline
swiftlint --write-baseline .swiftlint.baselineThis creates a JSON file listing every current violation by file, line, and rule. Commit this file to the repository.
Using a baseline
swiftlint --baseline .swiftlint.baselineViolations matching the baseline are suppressed. New violations (new files, new lines, new rules) are reported normally.
Updating a baseline
After fixing violations, regenerate:
swiftlint --write-baseline .swiftlint.baselineThe new baseline will be smaller. Commit the update.
Baseline in CI
swiftlint --strict --baseline .swiftlint.baseline --reporter github-actions-loggingThis fails the build only on new violations not present in the baseline.
Baseline vs suppressions
| Approach | When to use |
|---|---|
| Baseline | Adopting SwiftLint in a large existing codebase |
| Inline suppression | Specific intentional deviation from a rule |
disabled_rules | Team disagrees with a rule project-wide |
excluded paths | Generated or vendored code |
| Child config | Different rules for test vs production code |
False Positives
When SwiftLint flags code incorrectly:
1. Check if it's a real false positive by reading the rule description in the rule directory. 2. Check your config — threshold tuning may resolve it (e.g., raising line_length for a file with long URLs). 3. Suppress with a reason if it's genuinely incorrect. 4. File an issue at realm/SwiftLint if the false positive is reproducible and affects others.
Generated Code and Test Targets
Generated code
Exclude generated code directories in .swiftlint.yml:
excluded:
- "**/Generated"
- "**/Derived"
- "**/*.generated.swift"This is preferable to inline // swiftlint:disable all markers because it keeps generated files completely out of the lint pass.
Test targets
Tests legitimately use patterns that production code should avoid (force unwraps, long functions, etc.). Use a child .swiftlint.yml in the test directory:
# Tests/.swiftlint.yml
disabled_rules:
- force_unwrapping
- force_try
- force_cast
- function_body_length
- type_body_length
- file_lengthThis is better than excluding tests entirely, because tests still benefit from formatting rules, naming conventions, and other applicable checks.
Related skills
How it compares
Pick swiftlint for tooling setup and CI enforcement; pair with swift-api-design-guidelines for underlying Swift naming conventions.
FAQ
What does swiftlint do?
Configures and enforces SwiftLint in Swift projects using build tool plugins, run scripts, and CI. Covers .swiftlint.yml configuration, disabled_rules, opt_in_rules,.
When should I use swiftlint?
User setting up SwiftLint, configuring lint rules, suppressing warnings, creating baselines, choosing between build tool plugin and run script, or integrating SwiftLint into CI.
Is swiftlint safe to install?
Review the Security Audits panel on this page before installing in production.