如何使用 Swift 包管理器将 SwiftLint 与 iOS 应用集成?

How to integrate SwiftLint with an iOS app using Swift Package Manager?

我正在 Xcode 11(测试版 5)中创建一个新的 iOS 应用程序,我想尝试使用 Swift 包管理器而不是 CocoaPods 来管理依赖项。

使用 SwiftLint 和 CocoaPods 时的一个常见模式是添加 SwiftLint 作为依赖项,然后添加构建阶段以执行 ${PODS_ROOT}/SwiftLint/swiftlint;这样所有开发人员最终都使用相同版本的 SwiftLint。

如果我尝试在 Xcode 中添加 SwiftLint 作为 SwiftPM 依赖项,我需要的可执行目标被禁用:

我可以通过在构建阶段创建一个没有产品或目标的虚拟 Package.swift 和 运行 swift run swiftlint 来伪造它,但感觉很古怪和奇怪:

// swift-tools-version:5.1
import PackageDescription

let package = Package(
    name: "dummy-package",
    products: [],
    dependencies: [
        .package(url: "https://github.com/realm/SwiftLint.git", from: "0.34.0")
    ],
    targets: []
)

有没有不创建虚拟包的方法?或者 Swift 包管理器不是适合这个特定用例的工具?

所有 滥用 iOS 代码依赖管理器到 运行 构建工具的方法都很古怪。

对符合 SPM 的工具依赖项进行版本控制的正确方法是使用 Mint: A package manager that installs and runs Swift CLI packages. See also Better iOS projects: How to manage your tooling with mint

我使用 xcodegen 生成一个 Xcode 项目,该项目能够 运行 脚本。这让我在开发包时看到 Xcode 中的 swiftlint 警告。

此工具从 project.yml definition. In that definition, you can add a script that runs swiftlint as a post compile task. Example.

创建一个 Xcode 项目

这种方法的优点:

  • Xcode 中的 swiftlint 警告。
  • Xcode 超出 SPM 提供范围的设置。

缺点:

  • 您依赖的第三方工具可能会损坏或消失。但是,您可以随时删除此依赖项并返回编辑 Xcode.
  • 中的 Package.swift
  • 您需要学习编写 project.yml 个文件。
  • 如果您使用 SPM 包,您需要自己生成包访问器。

关于生成包访问器的一句话。从 Xcode 项目工作时需要这样做,因为只有 SPM 会为项目生成文件 resource_bundle_accessor.swift。如果你在用 Xcode 打开 Package.swift 后已经编译,文件应该在这里:

find ~/Library/Developer/Xcode/DerivedData* -iname resource_bundle_accessor.swift

您可以将它添加到项目中,但如果您正在创建一个框架,则 bundle 访问器可以像这样简单:

import class Foundation.Bundle

// This file is only used when using a xcodegen-generated project.
// Otherwise this file should not be in the path.

private class BundleFinder {}

extension Foundation.Bundle {
    static var module = Bundle(for: BundleFinder.self)
}

不是完美的解决方案,但它有效。我找到了 here

  1. 修改 Package.swift
  2. 的目标部分
targets: [
    // 1. Specify where to download the compiled swiftlint tool from.
    .binaryTarget(
        name: "SwiftLintBinary",
        url: "https://github.com/realm/SwiftLint/releases/download/0.47.1/SwiftLintBinary-macos.artifactbundle.zip",
        checksum: "cdc36c26225fba80efc3ac2e67c2e3c3f54937145869ea5dbcaa234e57fc3724"
    ),
    // 2. Define the SPM plugin.
    .plugin(
      name: "SwiftLintPlugin",
      capability: .buildTool(),
      dependencies: ["SwiftLintBinary"]
    ),
    // 3. Use the plugin in your project.
    .target(
        name: "##NameOfYourMainTarget",
        dependencies: ["SwiftLintPlugin"] // dependency on plugin
    )
  ]
  1. 创建Plugins/SwiftLintPlugin/SwiftLintPlugin.swift
import PackagePlugin

@main
struct SwiftLintPlugin: BuildToolPlugin {
  func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
    return [

        .buildCommand(
            displayName: "Running SwiftLint for \(target.name)",
            executable: try context.tool(named: "swiftlint").path,
            arguments: [
                "lint",
                "--in-process-sourcekit",
                "--path",
                target.directory.string,
                "--config",
                "\(context.package.directory.string)/.swiftlint.yml" // IMPORTANT! Path to your swiftlint config
            ],
            environment: [:]
        )
    ]
  }
}