ITADN
realm/SwiftLint
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈

SwiftLint

一个用于强制执行 Swift 风格和约定的工具,大致基于现已 归档的 GitHub Swift Style Guide。 SwiftLint 强制执行 Swift 社区普遍接受的风格指南规则。这些规则在诸如 Kodeco's Swift Style Guide 等流行风格指南中有详细描述。

SwiftLint 规则主要基于 SwiftSyntax。 一些规则仍然通过 ClangSourceKit 来访问类型信息。

Supported Swift Versions Supported Platforms Buildkite Build Status

SwiftLint violations highlighted in the Xcode editor

本项目遵循 贡献者公约行为准则。 参与即表示您同意遵守此准则。

切换语言: 中文, 한국어

视频介绍

为了对 SwiftLint 有一个高层次的了解,我们建议您观看 由 JP Simard 于 2017 年 1 月 9 日录制的这场演示(提供文字记录):

Presentation

安装

Swift Package Manager

SwiftLint 可以作为 command pluginbuild tool plugin 使用。

添加

.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "<version>")

在你的 Package.swift 文件中,以自动使用 SwiftLint 的最新版本,或将依赖项固定到特定版本:

.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", exact: "<version>")

其中,请将 <version> 替换为所需的最低版本或确切版本。

[!NOTE] 直接从 SwiftLint 仓库消费插件会带来 若干缺点。为避免这些问题并减少由此产生的开销,强烈建议 从专用的 SwiftLintPlugins 仓库 消费插件, 尽管来自 SwiftLint 仓库的插件也完全 可用。如果更倾向于使用 SwiftLint 的插件,只需在上述包声明中使用 URL https://github.com/realm/SwiftLint 即可。

然而,SwiftLintPlugins 极大地促进了插件的采用。它列举了一些原因, 说明为何 SwiftLint 自身提供的插件使用起来非常麻烦。由于 插件代码和发布版本保持同步,两者在功能上没有区别,但使用专用的插件仓库可以 为你节省大量时间和麻烦。

本文档假设你依赖的是 SwiftLintPlugins。

Xcode 包依赖

使用以下链接将 SwiftLint 作为包依赖添加到 Xcode 项目中:

https://github.com/SimplyDanny/SwiftLintPlugins

Homebrew

brew install swiftlint

CocoaPods

将以下内容添加到你的 Podfile 中:

pod 'SwiftLint'

这将在你下次执行 pod install 时下载 Pods/ 中的 SwiftLint 二进制文件和依赖项,并允许你在 Script Build Phases 中通过 ${PODS_ROOT}/SwiftLint/swiftlint 调用它。

通过 Cocoapods 安装还可以将 SwiftLint 固定到特定版本,而不仅仅是最新版本(Homebrew 的情况即如此)。

请注意,这会将 SwiftLint 二进制文件、其依赖项的二进制文件以及 Swift 二进制库分发版添加到 Pods/ 目录中,因此不建议将此目录检入 Git 等 SCM 系统。

Mint

mint install realm/SwiftLint

Bazel

将此放入你的 MODULE.bazel 中:

bazel_dep(name = "swiftlint", version = "0.52.4", repo_name = "SwiftLint")

或者将此放入你的 WORKSPACE 中:

工作区
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "build_bazel_rules_apple",
    sha256 = "390841dd5f8a85fc25776684f4793d56e21b098dfd7243cd145b9831e6ef8be6",
    url = "https://github.com/bazelbuild/rules_apple/releases/download/2.4.1/rules_apple.2.4.1.tar.gz",
)

load(
    "@build_bazel_rules_apple//apple:repositories.bzl",
    "apple_rules_dependencies",
)

apple_rules_dependencies()

load(
    "@build_bazel_rules_swift//swift:repositories.bzl",
    "swift_rules_dependencies",
)

swift_rules_dependencies()

load(
    "@build_bazel_rules_swift//swift:extras.bzl",
    "swift_rules_extra_dependencies",
)

swift_rules_extra_dependencies()

http_archive(
    name = "SwiftLint",
    sha256 = "c6ea58b9c72082cdc1ada4a2d48273ecc355896ed72204cedcc586b6ccb8aca6",
    url = "https://github.com/realm/SwiftLint/releases/download/0.52.4/bazel.tar.gz",
)

load("@SwiftLint//bazel:repos.bzl", "swiftlint_repos")

swiftlint_repos()

load("@SwiftLint//bazel:deps.bzl", "swiftlint_deps")

swiftlint_deps()

然后你可以使用以下命令在当前目录中运行 SwiftLint:

bazel run -c opt @SwiftLint//:swiftlint

预构建包

最新 GitHub 发布版本 下载 SwiftLint.pkg 并 运行它。

从源码构建

确保已安装构建工具 Bazel 和较新的 Swift 工具链,并且所有工具均可在您的 PATH 中发现。

要构建 SwiftLint,请克隆此仓库并运行 make install

设置

[!IMPORTANT] 虽然直觉上可能会认为在编译 Swift 源文件之前运行 SwiftLint,以便在存在 lint 违规时提前退出构建,但重要的是要理解 SwiftLint 旨在分析可编译的有效源代码。无法编译的代码很容易导致意外且 令人困惑的结果,尤其是在使用 --fix/--autocorrect 命令行参数执行时。

构建工具插件

SwiftLint 可以作为构建工具插件用于 Swift Package 项目Xcode 项目

构建工具插件通过定位包/项目目录内的最顶层配置文件来确定 SwiftLint 工作目录。如果未在其中找到配置文件,则使用包/项目目录作为工作 目录。

当插件无法解析 SwiftLint 工作目录时,它会抛出错误。例如,在 Xcode 项目中,如果目标的 Swift 文件未位于项目目录内,就会发生这种情况。

为了最大化与插件的兼容性,请避免使用需要 --config 选项的项目结构。

Swift Package 项目

[!NOTE] 需要通过 Swift Package Manager 进行安装。

构建工具插件在构建每个目标时运行。当项目包含多个 目标时,必须将插件单独添加到所需的目标中。

为此,请按照以下方式将插件添加到要执行 lint 的目标:

.target(
    ...
    plugins: [.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins")]
),

Swift Package 命令插件

[!NOTE] 需要通过 Swift Package Manager 进行安装。

该命令插件支持按以下方式从命令行运行 SwiftLint:

swift package plugin swiftlint

Xcode 项目

[!NOTE] 需要通过 Xcode Package Dependency 进行安装。

构建工具插件作为每个目标的构建阶段运行。当项目包含 多个目标时,必须将插件单独添加到所需的目标中。

为此,请将 SwiftLintBuildToolPlugin 添加到要执行 lint 的目标的 Run Build Tool Plug-ins 阶段的 Build Phases 中。

[!TIP] 首次使用该插件时,请务必在提示时信任并启用 它。如果存在宏构建警告,请选择它以信任并启用宏。

对于无人值守的使用(例如在 CI 上),可以通过以下任一方式禁用 包插件和宏验证:

  • 使用 xcodebuild 选项:

    -skipPackagePluginValidation
    -skipMacroValidation
  • 设置 Xcode 默认值:

    defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES
    defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES

[!IMPORTANT] 无人值守使用选项会绕过 Xcode 的验证对话框, 并隐式信任所有插件和宏,这具有安全影响。

意外的 Xcode 项目结构

SwiftLint 配置文件位于包/项目目录之外的项目结构 不受构建工具插件的直接支持。这是因为无法向 构建工具插件传递参数(例如,传递配置文件路径)。

如果你的项目结构无法直接与构建工具 插件配合使用,请考虑以下选项之一:

  • 若要使用位于包/项目目录之外的配置文件,可以在该目录中添加一个配置文件, 指定指向其他配置文件的父配置路径,例如,parent_config: path/to/.swiftlint.yml
  • 你也可以考虑使用 Run Script Build Phase 来替代构建 工具插件。

Xcode Run Script Build Phase

[!NOTE] 根据所使用的安装方法,Run Script Build Phase 中的 shell 命令语法可能不同, 或者可能需要额外的配置。请参阅 installation 说明以 获取更多信息。

如果构建工具插件不适用于你的项目配置,或者需要额外的自定义设置, 可以将 SwiftLint 添加为 Run Script Build Phase。当项目配置依赖于 --config SwiftLint 选项时,或者需要在单次 swiftlint 调用中一起检查所有目标时,这非常有用。文件的包含和排除可以在 .swiftlint.yml 配置 中进行配置。

为此,请在主应用目标的 Build PhasesRun Script 阶段中添加一个自定义脚本,位于 Compile Sources 阶段之后。使用 以下脚本实现:

if command -v swiftlint >/dev/null 2>&1
then
    swiftlint
else
    echo "warning: `swiftlint` command not found - See https://github.com/realm/SwiftLint#installation for installation instructions."
fi

如果你正在 Swift 包中使用 SwiftLintPlugin, 你可以以如下方式引用 swiftlint 可执行文件:

SWIFT_PACKAGE_DIR="${SWIFT_PACKAGE_DIR:-${BUILD_DIR%Build/*}SourcePackages}"
SWIFTLINT_CMD="$SWIFT_PACKAGE_DIR/artifacts/swiftlintplugins/SwiftLintBinary/SwiftLintBinary.artifactbundle/macos/swiftlint"

if test -f "$SWIFTLINT_CMD" 2>&1
then
    "$SWIFTLINT_CMD"
else
    echo "warning: `swiftlint` command not found - See https://github.com/realm/SwiftLint#xcode-run-script-build-phase for installation instructions."
fi

[!NOTE] SWIFTLINT_CMD 路径使用默认的 Xcode 配置,并已在 Xcode 15/16 上 进行测试。如果是其他配置(例如自定义 Swift 包路径),请相应地调整这些值。 如果你使用 -clonedSourcePackagesDirPath 运行 xcodebuild,请在 运行脚本之前将 SWIFT_PACKAGE_DIR 设置为 <that path>

[!TIP] 取消勾选 Based on dependency analysis 以在所有增量 构建上运行 swiftlint,从而抑制未指定输出的警告。

针对 Xcode 15.0 的注意事项

Xcode 15 进行了一项重大更改,将 ENABLE_USER_SCRIPT_SANDBOXING 构建设置的默认值从 NO 更改为 YES。 因此,SwiftLint 会遇到与文件权限缺失相关的错误, 通常表现为 error: Sandbox: swiftlint(19427) deny(1) file-read-data.

要解决此问题,需要手动将 ENABLE_USER_SCRIPT_SANDBOXING 设置设置为 NO,针对 正在配置 SwiftLint 的特定目标。

针对 Apple Silicon 的注意事项

如果你通过 Homebrew 在 Apple Silicon 上安装了 SwiftLint,你可能会遇到 此警告:

warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint

这是因为 Homebrew 在 Apple Silicon 上默认将二进制文件安装到 /opt/homebrew/bin 文件夹中。要指示 Xcode 在哪里找到 SwiftLint,您可以在构建阶段中将 /opt/homebrew/bin 添加到 PATH 环境变量:

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` command not found - See https://github.com/realm/SwiftLint#installation for installation instructions."
fi

或者你可以在 /usr/local/bin 中创建一个指向实际 二进制的符号链接:

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

其他注意事项

如果你希望同时修复违规项,你的脚本可以运行 swiftlint --fix && swiftlint 而不仅仅是 swiftlint。这意味着 所有可修复的违规项都将被修复,同时确保剩余违规项的警告 在你的项目中显示出来。

如果你已通过 CocoaPods 安装 SwiftLint,脚本应如下所示:

"${PODS_ROOT}/SwiftLint/swiftlint"

Visual Studio Code

要将 SwiftLint 集成到 Visual Studio Code,请从市场安装 vscode-swiftlint 扩展。

Fastlane

你可以使用官方的 swiftlint fastlane action 在 fastlane 流程中运行 SwiftLint。

swiftlint(
    mode: :lint,                            # SwiftLint mode: :lint (default) or :autocorrect
    executable: "Pods/SwiftLint/swiftlint", # The SwiftLint binary path (optional). Important if you've installed it via CocoaPods
    path: "/path/to/lint",                  # Specify path to lint (optional)
    output_file: "swiftlint.result.json",   # The path of the output file (optional)
    reporter: "json",                       # The custom reporter to use (optional)
    config_file: ".swiftlint-ci.yml",       # The path of the configuration file (optional)
    files: [                                # List of files to process (optional)
        "AppDelegate.swift",
        "path/to/project/Model.swift"
    ],
    ignore_exit_status: true,               # Allow fastlane to continue even if SwiftLint returns a non-zero exit status (Default: false)
    quiet: true,                            # Don't print status logs like 'Linting ' & 'Done linting' (Default: false)
    strict: true                            # Fail on warnings? (Default: false)
)

Docker

SwiftLint 也可作为 Docker 镜像使用 Ubuntu。因此,首次使用时需要使用以下命令拉取 Docker 镜像:

docker pull ghcr.io/realm/swiftlint:latest

然后,在后续的运行中,你只需在 docker 内部执行 swiftlint,例如:

docker run -it -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest

这将在你当前所在的文件夹(pwd)中执行 swiftlint, 显示如下输出:

$ docker run -it -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest
Linting Swift files in current working directory
Linting 'RuleDocumentation.swift' (1/490)
...
Linting 'YamlSwiftLintTests.swift' (490/490)
Done linting! Found 0 violations, 0 serious in 490 files.

这里有关于使用 Docker Images 的更多文档。

MegaLinter

SwiftLint 也集成在 MegaLinter 中,这是一个 用于 CI 的开源 linter 聚合器。请参阅其 SwiftLint 文档 以获取设置说明。

命令行用法

$ swiftlint help
OVERVIEW: A tool to enforce Swift style and conventions.

USAGE: swiftlint <subcommand>

OPTIONS:
  --version               Show the version.
  -h, --help              Show help information.

SUBCOMMANDS:
  analyze                 Run analysis rules
  docs                    Open SwiftLint documentation website in the default web browser
  generate-docs           Generates markdown documentation for selected group of rules
  lint (default)          Print lint warnings and errors
  baseline                Operations on existing baselines
  reporters               Display the list of reporters and their identifiers
  rules                   Display the list of rules and their identifiers
  version                 Display the current version of SwiftLint

  See 'swiftlint help <subcommand>' for detailed help.

在包含待检查 Swift 文件的目录中运行 swiftlint。目录 将被递归搜索。

若要在使用 lintanalyze 时 指定文件列表(例如由 ExtraBuildPhase Xcode 插件指定的 Xcode 修改的文件列表,或基于 git ls-files -m 的工作树中修改的文件), 您可以通过传递选项 --use-script-input-files 并设置 以下实例变量来实现:SCRIPT_INPUT_FILE_COUNTSCRIPT_INPUT_FILE_0SCRIPT_INPUT_FILE_1,..., SCRIPT_INPUT_FILE_{SCRIPT_INPUT_FILE_COUNT - 1}。 类似地,可以通过传递 选项 --use-script-input-file-lists 并设置 以下实例变量来从文件列表中读取文件:SCRIPT_INPUT_FILE_LIST_COUNTSCRIPT_INPUT_FILE_LIST_0SCRIPT_INPUT_FILE_LIST_1,..., SCRIPT_INPUT_FILE_LIST_{SCRIPT_INPUT_FILE_LIST_COUNT - 1}

这些是为输入文件设置的相同环境变量,用于 自定义 Xcode 脚本阶段

处理多个 Swift 版本

SwiftLint 与 SourceKit 集成,因此即使 Swift 不断演进,它也能继续工作!

这也使 SwiftLint 保持轻量,因为它不需要附带完整的 Swift 编译器,只需与您机器上已安装的官方编译器进行通信即可。

您应始终使用与编译代码相同的工具链来运行 SwiftLint。

如果您安装了多个工具链或 Xcode,您可能希望覆盖 SwiftLint 的默认 Swift 工具链。

以下是 SwiftLint 确定使用哪个 Swift 工具链的顺序:

  • $XCODE_DEFAULT_TOOLCHAIN_OVERRIDE
  • $TOOLCHAIN_DIR$TOOLCHAINS
  • xcrun -find swift
  • /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
  • /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
  • ~/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain
  • ~/Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain

预期在通过上述路径传递的值的 usr/lib/ 子目录中找到 sourcekitd.framework

您还可以将 TOOLCHAINS 环境变量设置为标识 Swift 工具链版本的反向 DNS 表示法:

TOOLCHAINS=com.apple.dt.toolchain.Swift_2_3 swiftlint --fix

在 Linux 上,SourceKit 预期位于 /usr/lib/libsourcekitdInProc.so 或由 LINUX_SOURCEKIT_LIB_PATH 环境变量指定。

Git pre-commit Hook

SwiftLint 可以作为 pre-commit hook 运行。 一旦 安装,请在仓库根目录的 .pre-commit-config.yaml 中添加以下内容:

repos:
  - repo: https://github.com/realm/SwiftLint
    rev: 0.57.1
    hooks:
      - id: swiftlint

rev 调整为您选择的 SwiftLint 版本。 pre-commit autoupdate 可用于更新到当前版本。

SwiftLint 可以使用 entry 进行配置,以应用修复并在出现错误时失败:

- repo: https://github.com/realm/SwiftLint
  rev: 0.57.1
  hooks:
    - id: swiftlint
      entry: swiftlint --fix --strict

规则

SwiftLint 包含超过 200 条规则,并且 Swift 社区(也就是你!) 会随着时间的推移持续贡献更多规则。 Pull requests 是受鼓励的。

你可以在 Rule Directory 中找到更新的规则列表以及关于它们的更多信息。

你还可以查看 Source/SwiftLintBuiltInRules/Rules 目录以查看它们的实现。

Opt-In Rules

opt_in_rules 默认是禁用的(即,你必须在配置文件中显式启用它们)。

关于何时将规则标记为 opt-in 的指南:

  • 可能产生许多误报的规则(例如 empty_count
  • 速度过慢的规则
  • 未达成普遍共识或仅在特定情况下有用的规则 (例如 force_unwrapping

Disable rules in code

可以通过在源文件内使用以下格式的注释来禁用规则:

// swiftlint:disable <rule1> [<rule2> <rule3>...]

规则将被禁用,直到文件末尾或 linter 看到匹配的启用注释:

// swiftlint:enable <rule1> [<rule2> <rule3>...]

例如:

// swiftlint:disable colon
let noWarning :String = "" // No warning about colons immediately after variable names.
// swiftlint:enable colon
let hasWarning :String = "" // Warning generated about colons immediately after variable names.

包含 all 关键字将禁用所有规则,直到 linter 看到匹配的启用注释:

// swiftlint:disable all // swiftlint:enable all

例如:

// swiftlint:disable all
let noWarning :String = "" // No warning about colons immediately after variable names.
let i = "" // Also no warning about short identifier names.
// swiftlint:enable all
let hasWarning :String = "" // Warning generated about colons immediately after variable names.
let y = "" // Warning generated about short identifier names.

也可以通过在 disableenable 命令后追加 :previous:this:next,使该命令仅分别应用于上一行、 当前行或下一行。

例如:

// swiftlint:disable:next force_cast
let noWarning = NSNumber() as! Int
let hasWarning = NSNumber() as! Int
let noWarning2 = NSNumber() as! Int // swiftlint:disable:this force_cast
let noWarning3 = NSNumber() as! Int
// swiftlint:disable:previous force_cast

运行 swiftlint rules 以打印所有可用规则及其 标识符的列表。

配置

通过从运行 SwiftLint 的目录添加 .swiftlint.yml 文件来配置 SwiftLint。可以配置以下参数:

规则包含:

  • disabled_rules:禁用默认启用集合中的规则。
  • opt_in_rules:启用不属于默认集合的规则。特殊的 all 标识符将启用所有可选的 linter 规则,但 disabled_rules 中列出的规则除外。
  • only_rules:仅启用此列表中指定的规则。 不能与 disabled_rulesopt_in_rules 同时指定。
  • analyzer_rules:这是一个完全独立的规则列表,仅由 analyze 命令运行。所有分析器规则均为可选,因此这是 唯一可配置的规则列表,没有 disabled_rulesonly_rules 的对应项。特殊的 all 标识符也可以在此处使用,以启用 所有分析器规则,但 disabled_rules 中列出的规则除外。
# By default, SwiftLint uses a set of sensible default rules you can adjust. Find all the available rules
# by running `swiftlint rules` or visiting https://realm.github.io/SwiftLint/rule-directory.html.

# Rules turned on by default can be disabled.
disabled_rules:
  - colon
  - comma
  - control_statement
  
# Rules turned off by default can be enabled.
opt_in_rules:
  - empty_count

# Alternatively, specify all rules explicitly by uncommenting this option and removing the above two.
# only_rules:
#   - empty_parameters
#   - vertical_whitespace

# Rules only run by `swiftlint analyze`. These are all opt-in.
analyzer_rules:
  - explicit_self

# Case-sensitive paths to include during linting. Directory paths supplied on the
# command line will be ignored. Wildcards are supported.
included: 
  - Sources

# Case-sensitive paths to ignore during linting. Takes precedence over `included`. Wildcards
# are supported.
excluded: 
  - Carthage
  - Pods
  - Sources/ExcludedFolder
  - Sources/ExcludedFile.swift
  - Sources/*/ExcludedFile.swift

# If true, SwiftLint will not fail if no lintable files are found.
allow_zero_lintable_files: false

# If true, SwiftLint will treat all warnings as errors.
strict: false

# If true, SwiftLint will treat all errors as warnings.
lenient: false

# The path to a baseline file, which will be used to filter out detected violations.
baseline: Baseline.json

# The path to save detected violations to as a new baseline.
write_baseline: Baseline.json

# If true, SwiftLint will check for updates after linting or analyzing.
check_for_updates: true

# Configurable rules can be customized. All rules support setting their severity level.
force_cast: warning # implicitly
force_try:
  severity: warning # explicitly
  
# Rules that have both warning and error levels can set just the warning level implicitly.
line_length: 110

# To set both levels implicitly, use an array.
type_body_length:
  - 300 # warning
  - 400 # error

# To set both levels explicitly, use a dictionary.
file_length:
  warning: 500
  error: 1200
  
# Naming rules can set warnings/errors for `min_length` and `max_length`. Additionally, they can
# set excluded names and allowed symbols.
type_name:
  min_length: 4 # warning
  max_length: # warning and error
    warning: 40
    error: 50
  excluded: i(Phone|Pad|Pod) # regex pattern
  allowed_symbols: ["_"]
identifier_name:
  min_length:
    error: 4 # only error
  excluded: # excluded via string array
    - id
    - URL
    - GlobalAPIKey
    
# The default reporter (SwiftLint's output format) can be configured as `checkstyle`, `codeclimate`, `csv`,
# `emoji`, `github-actions-logging`, `gitlab`, `html`, `json`, `junit`, `markdown`, `relative-path`, `sarif`,
# `sonarqube`, `summary`, or `xcode` (default).
reporter: "xcode"

你也可以在配置文件中通过字符串使用 ${SOME_VARIABLE} 来使用环境变量。

定义自定义规则

除了 SwiftLint 主项目自带的规则外,SwiftLint 还可以运行两种你可以在自己的项目中自行定义的自定义规则:

1. Swift 自定义规则

这些规则的编写方式与 SwiftLint 自带的基于 Swift 的规则相同,因此它们速度快、准确,可以利用 SwiftSyntax,可以进行单元测试,等等。

使用这些规则需要按照 此视频 或其相关代码 github.com/jpsim/swiftlint-bazel-example 中所述使用 Bazel 构建 SwiftLint。

2. 正则表达式自定义规则

你可以在配置文件中使用以下语法定义基于正则表达式的自定义规则:

custom_rules:
  # Rule identifier.
  pirates_beat_ninjas:
    # Optional regex that defines paths to include during linting.
    included:
      - ".*\\.swift"
    # Optional regex that defines paths to exclude during linting.
    excluded:
      - ".*Test\\.swift"
    # Optional rule name.
    name: "Pirates Beat Ninjas"
    # Matching pattern.
    regex: "([nN]inja)"
    # Number of regex capture group to highlight the rule violation at. Optional, defaults to 0 (the whole match).
    capture_group: 0
    # SyntaxKinds to match. optional.
    match_kinds:
      - comment
      - identifier
    # Optional violation message.
    message: "Pirates are better than ninjas."
    # Optional violation severity.
    severity: error
  no_hiding_in_strings:
    regex: "([nN]inja)"
    # Syntax kinds to match. optional.
    match_kinds: string

这就是输出的样子:

Custom violations highlighted in the Xcode editor

需要注意的是,正则表达式模式在使用时启用了 sm 标志,即 . 匹配换行符 以及 ^/$ 分别匹配行首和行尾。 例如,如果你不希望 . 匹配换行符,可以在 正则表达式前添加 (?-s)

你可以通过提供一个或多个 match_kinds 来过滤匹配结果,这将 拒绝包含不在该列表中的语法类型的匹配。以下是所有可能的语法类型:

  • 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

Swift 代码片段中使用的所有语法种类都可以通过询问 SourceKitten 来提取。例如, sourcekitten syntax --text "struct S {}" 返回

  • source.lang.swift.syntaxtype.keyword 对应 struct 关键字,以及
  • source.lang.swift.syntaxtype.identifier 对应其名称 S

它们分别匹配上述列表中的 keywordidentifier

如果将自定义规则与 only_rules 结合使用,则必须在 only_rules 列表中包含字面字符串 custom_rules

only_rules:
  - custom_rules

custom_rules:
  no_hiding_in_strings:
    regex: "([nN]inja)"
    match_kinds: string

与 Swift 自定义规则不同,你可以使用官方的 SwiftLint 构建版本 (例如来自 Homebrew 的版本)来运行正则表达式自定义规则。

自动更正

SwiftLint 可以自动纠正某些违规。磁盘上的文件 会被覆盖为已纠正的版本。

在运行 swiftlint --fix 之前,请确保已备份这些文件,否则可能会丢失重要数据。

在应用修正时,由于修改文件后违规项(或其偏移量)很可能不正确,因此在修正过程中会禁用标准 linting。

分析

swiftlint analyze 命令可以使用完整的类型检查 AST 对 Swift 文件进行 lint。 必须通过 --compiler-log-path 标志将包含干净 swiftc 构建命令调用的编译器日志路径传递给 analyze (增量构建将会失败)。 例如 --compiler-log-path /path/to/xcodebuild.log

这可以通过

  1. Cleaning DerivedData (incremental builds won't work with analyze)
  2. Running xcodebuild -workspace {WORKSPACE}.xcworkspace -scheme {SCHEME} > xcodebuild.log
  3. Running swiftlint analyze --compiler-log-path xcodebuild.log

分析器规则通常比 lint 规则慢得多。

使用多个配置文件

SwiftLint 提供了多种方式来包含多个配置文件。 多个配置文件会被合并为一个单一的配置, 然后像单个配置文件一样被应用。

存在许多使用场景,在这些场景中,使用多个配置文件可能很有帮助:

例如,可以使用团队范围内的共享 SwiftLint 配置,同时允许通过子配置文件在每个项目中覆盖设置。

团队级配置:

disabled_rules:
  - force_cast

项目特定配置:

opt_in_rules:
  - force_cast

子/父配置(本地)

你可以在配置文件中指定 child_config 和/或 parent_config 引用。这些引用应该是相对于其所在配置文件所在文件夹的本地路径。只要没有循环和歧义,这甚至可以递归工作。

子配置被视为细化,因此具有更高的优先级,而父配置在发生冲突时被视为具有较低优先级的基础。

下面是一个示例,假设你有以下文件结构:

ProjectRoot
    |_ .swiftlint.yml
    |_ .swiftlint_refinement.yml
    |_ Base
        |_ .swiftlint_base.yml

要同时包含细化文件和基础文件,您的 .swiftlint.yml 应 如下所示:

child_config: .swiftlint_refinement.yml
parent_config: Base/.swiftlint_base.yml

在合并父级和子级配置时,includedexcluded 配置 会被仔细处理,以考虑包含这些配置文件的目录位置差异。

路径合并遵循以下规则:

  1. 每个配置中的 included/excluded 条目将相对于该 配置文件的目录进行解析。
  2. 子级条目会覆盖冲突的父级条目。
  3. 合并后的路径列表计算如下:
merged.included = (parent.included - child.excluded) + child.included
merged.excluded = (parent.excluded - child.included) + child.excluded

这意味着子配置可以重新包含被其父配置排除的路径,并且可以排除被其父配置包含的路径。

子/父配置(远程)

正如你可以提供本地 child_config/parent_config 引用一样, 除了引用本地路径外,你还可以直接放置指向 配置文件的 URL。为了让 SwiftLint 检测到这些远程引用, 它们必须以 http://https:// 开头。

所引用的远程配置文件甚至可以递归地引用其他 远程配置文件,但不允许包含本地引用。

使用远程引用,你的 .swiftlint.yml 可以如下所示:

parent_config: https://myteamserver.com/our-base-swiftlint-config.yml

每次运行 SwiftLint 且拥有互联网连接时,SwiftLint 都会尝试 获取每个被引用的远程配置的新版本。如果此 请求超时,则使用缓存版本(如果可用)。如果没有可用的缓存 版本,SwiftLint 将失败——但请放心,一旦 SwiftLint 成功运行过至少一次,缓存版本 就应该存在。

如有需要,可以通过配置文件手动指定 远程配置获取的超时时间,使用 remote_timeout/remote_timeout_if_cached 说明符。这些值默认 分别为 2 秒或 1 秒。

命令行

在通过命令行运行 SwiftLint 时,除了仅提供一个配置文件外, 你还可以传递一个层级结构,其中第一个配置 被视为父级,而最后一个配置被视为最高优先级的 子级。

一个仅包含两个配置文件的简单示例如下:

swiftlint --config .swiftlint.yml --config .swiftlint_child.yml

嵌套配置

除了主配置(根目录中的 .swiftlint.yml 文件)外,你还可以将其他名为 .swiftlint.yml 的配置文件放入目录结构中。对于给定的文件,SwiftLint 会从该文件所在的目录向上遍历至根配置,并使用找到的第一个嵌套 .swiftlint.yml 作为子配置。

该嵌套配置仅适用于其目录子树中的文件。其他子树中的文件将继续使用主配置(或它们自己最近的嵌套配置)。

由于 SwiftLint 在向上遍历目录树时会在第一个匹配项处停止,每个文件最多合并一个嵌套配置(除了主配置之外)。

只有当 .swiftlint.yml 文件尚未被用于构建主配置时(例如,通过类似 child_config: Folder/.swiftlint.yml 的方式被引用),才会被视为嵌套配置。 此外,嵌套配置的 parent_config/child_config 规范会被忽略,因为这样做没有意义。

如果一个(或多个)SwiftLint 文件通过 --config 参数显式指定,无论目录中是否存在其他 .swiftlint.yml 文件,该配置都将被视为覆盖配置。 因此,如果你想使用嵌套配置,就不能使用 --config 参数。

许可证

MIT 许可证。

关于

SwiftLint 完全由志愿者在其空闲时间贡献以维持其成功。因此,SwiftLint 在任何方面都不是商业产品。

请善待那些以业余爱好维护 SwiftLint 的人们,并接受他们时间有限的事实。通过为项目做贡献、报告问题以及帮助社区中的其他人来支持他们。

特别感谢 MacStadium 提供物理 Mac mini 机器来运行我们的性能测试。

MacStadium

我们也感谢 Realm(现为 MongoDB)对该项目的初始贡献和搭建。