You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Xcode检测协议遵守者的loadOrder变量重复并抛出报错/警告?

实现Xcode检测协议遵守者的loadOrder重复并抛出警告/错误

可以通过两种主流方式实现这个需求,都是在编译阶段就能让Xcode抛出警告或错误,避免运行时才发现问题:

一、用SwiftLint自定义静态分析规则

SwiftLint是Xcode常用的代码静态检查工具,支持自定义规则,能直接在IDE里标记出重复的loadOrder。

步骤1:定义Loadable协议

先明确协议要求,确保所有需要排序的类都遵守这个协议:

protocol Loadable {
    static var loadOrder: Int { get }
}

// 默认实现,给不需要自定义顺序的类用
extension Loadable {
    static var loadOrder: Int { 9999 }
}

步骤2:编写自定义SwiftLint规则

创建一个LoadOrderDuplicateRule.swift文件,实现规则逻辑:

import SwiftLintCore

struct LoadOrderDuplicateRule: Rule {
    var configuration = SeverityConfiguration(.warning)

    static let description = RuleDescription(
        identifier: "load_order_duplicate",
        name: "Load Order Duplicate",
        description: "Detects duplicate non-default loadOrder values among Loadable protocol conformers.",
        kind: .lint,
        nonTriggeringExamples: [
            Example("class A: Loadable { static let loadOrder = 1 }"),
            Example("class B: Loadable { }"), // 使用默认9999
            Example("class C: Loadable { static let loadOrder = 2 }")
        ],
        triggeringExamples: [
            Example("class A: Loadable { static let loadOrder = 1 }"),
            Example("class B: Loadable { static let loadOrder = 1 }")
        ]
    )

    func validate(file: SwiftLintFile) -> [StyleViolation] {
        var orderMap = [Int: [String]]()
        // 筛选所有遵守Loadable协议的类型
        let loadableConformances = file.protocolConformances.filter {
            $0.protocolName == "Loadable"
        }

        for conformance in loadableConformances {
            guard let typeName = conformance.typeName.name else { continue }
            // 查找当前类型中名为loadOrder的静态属性
            let staticProperties = file.structureDictionary
                .children
                .compactMap { $0 as? [String: Any] }
                .filter {
                    let kind = $0["key.kind"] as? String
                    return kind == "source.lang.swift.decl.class" || kind == "source.lang.swift.decl.struct"
                }
                .filter { ($0["key.name"] as? String) == typeName }
                .flatMap { $0["key.children"] as? [[String: Any]] ?? [] }
                .filter {
                    $0["key.kind"] as? String == "source.lang.swift.decl.var.static" &&
                    ($0["key.name"] as? String) == "loadOrder"
                }

            // 提取loadOrder的数值
            for property in staticProperties {
                guard let valueNodes = property["key.children"] as? [[String: Any]],
                      let constantNode = valueNodes.first(where: {
                          $0["key.kind"] as? String == "source.lang.swift.expr.constant"
                      }),
                      let valueStr = constantNode["key.value"] as? String,
                      let orderValue = Int(valueStr) else { continue }

                // 跳过默认值9999的重复检查
                guard orderValue != 9999 else { continue }

                if orderMap[orderValue] == nil {
                    orderMap[orderValue] = [typeName]
                } else {
                    orderMap[orderValue]?.append(typeName)
                }
            }
        }

        // 生成违规提示
        var violations = [StyleViolation]()
        for (order, types) in orderMap where types.count > 1 {
            let errorMsg = "Load order \(order) is duplicated in types: \(types.joined(separator: ", "))"
            for typeName in types {
                if let conformance = loadableConformances.first(where: { $0.typeName.name == typeName }),
                   let location = conformance.location {
                    violations.append(StyleViolation(
                        ruleDescription: Self.description,
                        severity: configuration.severity,
                        location: location,
                        reason: errorMsg
                    ))
                }
            }
        }

        return violations
    }
}

步骤3:配置SwiftLint启用规则

在项目根目录的.swiftlint.yml文件中添加自定义规则配置:

custom_rules:
  load_order_duplicate:
    path: ./Scripts/LoadOrderDuplicateRule.swift # 替换成你的规则文件路径
    severity: warning # 可以改成error直接阻断编译

之后只要运行SwiftLint(或者把SwiftLint集成到Xcode的Build Phases),Xcode就会在有重复loadOrder的地方标记警告/错误。

二、用编译期脚本(基于SwiftSyntax)

如果不想依赖SwiftLint,可以用SwiftSyntax写一个脚本,在Xcode构建阶段执行,直接输出错误信息让Xcode识别。

步骤1:创建检查脚本

创建一个CheckLoadOrder.swift文件,用SwiftSyntax解析代码:

import Foundation
import SwiftSyntax

func checkLoadOrder(in directory: String) {
    let fileManager = FileManager.default
    var orderMap = [Int: [String]]()

    // 遍历所有.swift文件
    let enumerator = fileManager.enumerator(atPath: directory)!
    for case let file as String in enumerator where file.hasSuffix(".swift") {
        let filePath = URL(fileURLWithPath: directory).appendingPathComponent(file).path
        let sourceFile = try! SyntaxParser.parse(filePath)

        // 遍历所有类型声明
        sourceFile.walk { node -> SyntaxVisitorContinueKind in
            guard let typeDecl = node as? TypeDeclSyntaxProtocol else { return .visitChildren }
            let typeName = typeDecl.name.text

            // 检查是否遵守Loadable协议
            let conformsToLoadable = typeDecl.inheritanceClause?.inheritedTypes.contains {
                $0.typeName.text == "Loadable"
            } ?? false
            guard conformsToLoadable else { return .visitChildren }

            // 查找loadOrder静态属性
            if let classDecl = typeDecl as? ClassDeclSyntax {
                for member in classDecl.members {
                    guard let varDecl = member.decl as? VariableDeclSyntax,
                          varDecl.modifiers.contains(where: { $0.name.text == "static" }),
                          let binding = varDecl.bindings.first,
                          binding.pattern.text == "loadOrder",
                          let initializer = binding.initializer,
                          let integerLiteral = initializer.value as? IntegerLiteralExprSyntax,
                          let orderValue = Int(integerLiteral.digits.text) else { continue }

                    // 跳过默认值9999
                    guard orderValue != 9999 else { continue }

                    if orderMap[orderValue] == nil {
                        orderMap[orderValue] = [typeName]
                    } else {
                        orderMap[orderValue]?.append(typeName)
                    }
                }
            }
            return .visitChildren
        }
    }

    // 输出错误信息
    for (order, types) in orderMap where types.count > 1 {
        print("error: Load order \(order) is duplicated in types: \(types.joined(separator: ", "))")
    }
}

// 传入项目源码目录
checkLoadOrder(in: "./YourProjectSources") // 替换成你的源码路径

步骤2:集成到Xcode Build Phases

  1. 在Xcode项目中添加一个新的Run Script Build Phase
  2. 输入脚本命令:swift ./Scripts/CheckLoadOrder.swift(替换成你的脚本路径)
  3. 确保这个脚本在Compile Sources之前执行

这样每次构建时,脚本会自动检查,一旦发现重复的loadOrder,Xcode会在Issue Navigator里显示错误,甚至可以阻断编译。

补充说明

  • 如果需要允许默认值9999重复,两种方案里都已经做了过滤;如果连默认值也不想重复,删掉判断orderValue != 9999的逻辑即可。
  • SwiftLint方案的优势是能在编写代码时实时提示,体验更好;脚本方案更轻量,不需要依赖第三方工具。

内容的提问来源于stack exchange,提问作者Tiny Tim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 20:40:30