SwiftLint

repository·main·Indexed 12 days ago

https://github.com/realm/swiftlint

A tool based on SwiftSyntax used to enforce Swift style and conventions to maintain code quality. It supports installation via Swift Package Manager, Homebrew, Bazel, Docker, and Xcode Package Dependencies, and can be integrated as a build tool plugin, Xcode run script, Fastlane action, or pre-commit hook.

Tokens
12.2K
Snippets
51
Records
61
Agent score
97%

What's inside SwiftLint

  1. How nested configuration works

    main

    SwiftLint supports hierarchical configuration. You can place .swiftlint.yml files in different subdirectories to apply specific rules to specific parts of your project.

    • A file is linted using the .swiftlint.yml in its own directory, or the nearest .swiftlint.yml found in a parent directory.
    • If no configuration file is found in the hierarchy, the root configuration is used.
    • Important: In nested configurations, the included and excluded keys are ignored.
  2. How SwiftLint selects a Swift Toolchain

    main

    SwiftLint connects to SourceKit to analyze code. It uses the same toolchain that compiles your Swift files. If you have multiple Xcode versions or need to use a specific Swift version, SwiftLint determines the toolchain in the following order:

    1. $XCODE_DEFAULT_TOOLCHAIN_OVERRIDE environment variable
    2. $TOOLCHAIN_DIR or $TOOLCHAINS environment variables
    3. xcrun -find swift
    4. /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
    5. /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
    6. ~/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
    7. ~/Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain

    You can force a specific toolchain using the TOOLCHAINS environment variable:

    TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3 swiftlint autocorrect
  3. Use multiple configuration files via child and parent references

    main

    SwiftLint allows merging multiple configuration files into a single configuration. You can use child_config and parent_config keys within a .swiftlint.yml file to create a hierarchy.

    • child_config: Acts as a refinement and has higher priority in case of conflicts.
    • parent_config: Acts as a base with lower priority.

    Paths for these references must be local paths relative to the directory of the configuration file they are defined in. This supports recursive referencing as long as there are no cycles.

    Path Merging Rules for included and excluded:

    1. Entries are resolved relative to the directory of the config file they are in.
    2. Child entries override conflicting parent entries.
    3. The merged lists are calculated as:
      • merged.included = (parent.included - child.excluded) + child.included
      • merged.excluded = (parent.excluded - child.included) + child.excluded
    # .swiftlint.yml
    child_config: .swiftlint_refinement.yml
    parent_config: Base/.swiftlint_base.yml
  4. Use nested configuration files

    main

    SwiftLint automatically detects nested .swiftlint.yml files within your directory structure.

    How it works:

    • For any given file, SwiftLint walks up the directory tree towards the root. It uses the first .swiftlint.yml it encounters as a child configuration.
    • A nested configuration applies only to the files in its specific directory subtree.
    • At most one nested configuration is merged per file (in addition to the main root configuration).

    Important Constraints:

    • A .swiftlint.yml file is only treated as a nested configuration if it hasn't already been used to build the main configuration (e.g., via an explicit child_config reference).
    • If you use the --config parameter on the command line, nested configurations are ignored. The explicit --config file will act as an override for the entire run.
  5. Configure Swift toolchains for SwiftLint

    main

    SwiftLint uses SourceKit to communicate with the Swift compiler. It is recommended to run SwiftLint with the same toolchain used to compile your code.

    If you have multiple toolchains or Xcode versions, you can override the default toolchain. SwiftLint determines the toolchain in this order:

    1. $XCODE_DEFAULT_TOOLCHAIN_OVERRIDE
    2. $TOOLCHAIN_DIR or $TOOLCHAINS
    3. xcrun -find swift
    4. Standard Xcode application paths (e.g., /Applications/Xcode.app/...)

    You can specify a specific toolchain using the TOOLCHAINS environment variable with reverse-DNS notation.

    TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3 swiftlint --fix
  6. Integrate SwiftLint as a Swift Package Manager Plugin

    main

    If you are working with a Swift Package (containing a Package.swift manifest), you can add SwiftLint as a plugin.

    Limitation: Due to SPM plugin restrictions, it is recommended to only use this in projects where the SwiftLint configuration file is in the root directory, as you cannot pass additional command-line options to the executable via the plugin.

    .target(
        ...
        plugins: [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")]
    ),
  7. Integrate SwiftLint into Xcode Build Phases

    main

    To display SwiftLint warnings and errors directly in the Xcode IDE, add a new "Run Script Phase" to your target's "Build Phases" tab.

    Standard Script

    Use this script to check if SwiftLint is installed before running it:

    if which swiftlint >/dev/null; then
      swiftlint
    else
      echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
    fi

    For Apple Silicon (M1/M2/M3) Users

    If you installed SwiftLint via Homebrew on Apple Silicon, you must add /opt/homebrew/bin to your PATH so Xcode can locate the binary:

    if [[ "$(uname -m)" == arm64 ]]; then
        export PATH="/opt/homebrew/bin:$PATH"
    fi
    
    if which swiftlint > /dev/null; then
      swiftlint
    else
      echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
    fi

    Alternatively, you can create a symbolic link to /usr/local/bin:

    ln -s /opt/homebrew/bin/swiftlint /usr/local/bin/swiftlint

    Using CocoaPods Installation

    If you installed SwiftLint via CocoaPods, use this script in your Build Phase instead:

    "${PODS_ROOT}/SwiftLint/swiftlint"

    Pro-tips

    • Auto-fix violations: To automatically fix fixable violations and then show remaining warnings, use swiftlint --fix && swiftlint in your script.
    • Execution Order: It is recommended to run SwiftLint after the 'Compile Sources' phase. Running it before compilation might result in inaccurate errors because SwiftLint is designed to work with valid Swift code that has undergone parsing.
  8. Install SwiftLint via CocoaPods

    main

    Add SwiftLint to your Podfile. This method is recommended because it allows you to specify a specific version of SwiftLint, whereas Homebrew only provides the latest version.

    When installed via CocoaPods, the binary and its dependencies are placed in the Pods/ directory. You can access the executable in Xcode Build Phases using the path: ${PODS_ROOT}/SwiftLint/swiftlint.

    Note: It is not recommended to check the Pods/ directory (containing the SwiftLint binary and dependencies) into your SCM (e.g., Git).

    pod 'SwiftLint'
  9. Configure SwiftLint as a pre-commit hook

    main

    You can integrate SwiftLint into your workflow using the pre-commit framework. Add the SwiftLint repository to your .pre-commit-config.yaml file. You can customize the behavior using the entry key, such as applying automatic fixes with --fix or enforcing strict mode with --strict.

    # Basic configuration
    repos:
      - repo: https://github.com/realm/SwiftLint
        rev: 0.57.1
        hooks:
          - id: swiftlint
    
    # Configuration with automatic fixes and strict mode
    - repo: https://github.com/realm/SwiftLint
      rev: 0.57.1
      hooks:
        - id: swiftlint
          entry: swiftlint --fix --strict
  10. Integrate SwiftLint into Xcode via Run Script Phase

    main

    To display SwiftLint warnings and errors directly in the Xcode IDE, add a new "Run Script Phase" to your target and include the SwiftLint execution command.

    Important for Xcode 15+: Xcode 15 changed the default value of ENABLE_USER_SCRIPT_SANDBOXING to YES. This causes permission errors like error: Sandbox: swiftlint(19427) deny(1) file-read-data.. To fix this, manually set ENABLE_USER_SCRIPT_SANDBOXING to NO in your target's Build Settings.

    Handling Apple Silicon (M1/M2/M3) Homebrew installations: If SwiftLint is installed via Homebrew on Apple Silicon, it is located in /opt/homebrew/bin. You must add this to your PATH in the script phase so Xcode can find it.

    if [[ "$(uname -m)" == arm64 ]]; then
        export PATH="/opt/homebrew/bin:$PATH"
    fi
    
    if which swiftlint > /dev/null; then
      swiftlint
    else
      echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
    fi