🔑

Fixing SwiftLint Configuration for Monorepos in VSCode & Cursor

(Updated on )

TL;DR: When opening a subdirectory of a Swift Monorepo in VSCode or Cursor, the editor often fails to locate the root .swiftlint.yml. The solution lies in explicitly defining swiftlint.configSearchPaths in .vscode/settings.json and avoiding wildcard paths in your lint configuration.

When managing Swift projects with a Monorepo (Monolithic Repository) structure, it is best practice to maintain a single, unified .swiftlint.yml configuration at the root level. However, if you use VSCode, Cursor (or its VSCode-forked counterparts) to open a specific sub-package directory directly, the SwiftLint extension often fails to traverse up the directory tree to find the configuration, causing linting rules to break.

This guide explains how to configure your paths correctly to ensure SwiftLint works seamlessly at any level of your Monorepo.

Prerequisites

  • Tool: SwiftLint CLI installed.
  • Extension: The SwiftLint extension for VSCode/Cursor.
  • Architecture: A typical Swift Monorepo structure, as shown below:
Text
MyProject/                  
├── .swiftlint.yml          // Unified root config
├── .vscode/settings.json   // Root editor settings
├── Domain/
│   └── Package.swift
│   └── .vscode/settings.json // Sub-package editor settings
└── Persistent/
    └── Package.swift
    └── .vscode/settings.json

The Solution

To solve this, we need to coordinate changes between the VSCode settings file and the SwiftLint configuration file.

1. Root Directory Editor Settings

In the .vscode/settings.json file located in your project root, force the extension to look for the configuration file specifically. This ensures that when you open the root folder, the extension locks onto the correct config.

JSON
{
    "swiftlint.configSearchPaths": [
        ".swiftlint.yml"
    ]
}

2. Adjusting .swiftlint.yml (Crucial)

In your root .swiftlint.yml, you must explicitly list the sub-directories you want to lint.

❌ Avoid: Relying on default scanning or using vague wildcards. In some extension implementations, this can lead to path resolution errors.

✅ Recommended: Explicitly specify the included paths:

YAML
included:
  - Domain/Sources
  - Persistent/Sources
  # - FeatureA/Sources

3. Sub-Package Editor Settings (The Fix for Sub-directories)

This is the step most developers overlook. If you frequently open the Domain or Persistent folders directly in VSCode to work on them in isolation, you need to create a .vscode/settings.json inside those sub-directories as well.

You must point the search path to the parent configuration:

JSON
{
    "swiftlint.configSearchPaths": [
        "../.swiftlint.yml"
    ]
}

By setting ../.swiftlint.yml, even if your workspace root is Domain, the extension can “climb up” to find the parent directory’s configuration file.

Supplement: SwiftFormat Configuration

If you also utilize SwiftFormat, its configuration is generally smarter about path resolution. However, explicitly setting the path is still a safe bet. You typically don’t need to hardcode source paths in the config file itself, just point to the config location:

JSON
// Root directory .vscode/settings.json
"swiftformat.configSearchPaths": [".swiftformat"]

// Sub-directory .vscode/settings.json
"swiftformat.configSearchPaths": ["../.swiftformat"]
Related Tips

Subscribe to Fatbobman

Weekly Swift & SwiftUI highlights. Join developers.

Subscribe Now