TL;DR: SwiftLint may fail to apply lint rules in monorepo setups when packages are opened individually in VSCode or Cursor. To fix this, configure
swiftlint.configSearchPaths
in both root and sub-package.vscode/settings.json
files to correctly locate the shared.swiftlint.yml
. Also, explicitly defineincluded
paths in the config file (wildcards won’t work) to ensure proper linting across all modules.
Background
When managing Swift projects using a monorepo structure, developers often create a single .swiftlint.yml
configuration file in the root directory. However, when opening the project root or a specific Package directory in VSCode or Cursor, SwiftLint may fail to load the configuration properly, causing lint rules to be ignored. This article explains how to correctly configure SwiftLint in a monorepo setup to ensure linting works as expected.
Prerequisites
Environment Requirements
- SwiftLint CLI installed
- SwiftLint extension installed in VSCode or Cursor
Example Project Structure
MyProject/
├── .swiftlint.yml // Root-level SwiftLint config
├── .vscode/settings.json // Root-level VSCode settings
├── Domain/
│ └── Package.swift
│ └── .vscode/settings.json
└── Persistent/
└── Package.swift
└── .vscode/settings.json
Configuration Steps
VSCode Settings at the Root Level
Add the SwiftLint config search path to .vscode/settings.json
in the root directory:
"swiftlint.configSearchPaths": [".swiftlint.yml"]
.swiftlint.yml Configuration Notes
The included
section must explicitly specify all lint target directories. Wildcards are not supported, which is a common cause of misconfiguration:
included:
- Domain/Sources
- Persistent/Sources
With this setup, SwiftLint will correctly lint the specified directories when opening the full MyProject
directory.
VSCode Settings for Each Package
If you often open individual package directories like Domain/
or Persistent/
for development, add the following configuration in each package’s .vscode/settings.json
:
"swiftlint.configSearchPaths": ["../.swiftlint.yml"]
This allows SwiftLint to locate and apply the shared configuration file from the parent directory, even when working inside sub-packages.
SwiftFormat Configuration
SwiftFormat follows a similar configuration pattern but does not require specifying target directories inside the config file. Only the search path needs to be set:
// Root-level settings.json
"swiftformat.configSearchPaths": [".swiftformat"],
// Per-package settings.json
"swiftformat.configSearchPaths": ["../.swiftformat"]