SwiftLint 使用及原理

簡(jiǎn)介

SwiftLint 是 realm 公司開(kāi)發(fā)的一個(gè)插件,用于強(qiáng)制檢查 Swift 代碼風(fēng)格和規(guī)則的一個(gè)工具。
SwiftLint 的工作原理是檢查 Swift 代碼編譯過(guò)程中的 AST 和 SourceKit 環(huán)節(jié),從而可以擺脫不同版本 Swift 語(yǔ)法變化的影響。AST 是編譯前端形成的抽象語(yǔ)法樹(shù)(Abstract Symbolic Tree), SourceKit 過(guò)程用來(lái)對(duì) AST 進(jìn)行代碼優(yōu)化,減少內(nèi)存開(kāi)銷(xiāo),提高執(zhí)行效率。

安裝

1. 使用 Homebrew:
brew install swiftlint

2. 使用 CocoaPods:
將如下代碼添加到你的 Podfile 即可:
pod 'SwiftLint'

3. 使用安裝包:
通過(guò)從 最新的 GitHub 發(fā)布地址 下載 SwiftLint.pkg 然后執(zhí)行的方式安裝 SwiftLint。

用法

1. Xcode:
Xcode-Project-Build Phases-+(New Run Script Phase) 中添加一個(gè)新的 Script Phase 并且包含如下代碼即可:

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

2. 命令行:
在有需要執(zhí)行代碼分析的 Swift 源碼文件的目錄下執(zhí)行 swiftlint 命令,會(huì)對(duì)目錄遞歸查找。

$ swiftlint help
   autocorrect  Automatically correct warnings and errors
   help         Display general or command-specific help
   lint         Print lint warnings and errors for the Swift files in the current directory (default command)
   rules        Display the list of rules and their identifiers
   version      Display the current version of SwiftLint

規(guī)則

1. 終端執(zhí)行命令: swiftlint rules 或者 官方文檔: rule-directory

opt-in:  為 yes 時(shí),規(guī)則默認(rèn)不生效,需要添加進(jìn)配置的 opt_in_rules 時(shí)生效
correctable: 為 yes 時(shí),執(zhí)行 swiftlint autocorrect 命令,會(huì)自動(dòng)修改代碼格式為正確的格式
enabled in your config: 為 yes 時(shí),規(guī)則默認(rèn)生效,不需要額外配置
kind: 規(guī)則類(lèi)型,僅用于規(guī)則的分類(lèi) 
analyzer: 為 yes 時(shí),可在 Xcode 的 Analyze 生效
configuration: 支持配置的屬性默認(rèn)值。例: warning 代表不符合代碼規(guī)則時(shí),編譯器會(huì)警告。可在配置文件中改成 error,編譯器會(huì)報(bào)錯(cuò)

rules 列表詳見(jiàn)文章底部 附錄 1. swiftlint rules

2. 規(guī)則開(kāi)啟與關(guān)閉
可以通過(guò)在一個(gè)源文件中定義一個(gè)如下格式的注釋來(lái)關(guān)閉某個(gè)規(guī)則:
// swiftlint:disable <rule>
在該文件結(jié)束之前或者在定義如下格式的匹配注釋之前,這條規(guī)則都會(huì)被禁用:
// swiftlint:enable <rule>
也可以通過(guò)添加 :previous, :this 或者 :next 來(lái)使關(guān)閉或者打開(kāi)某條規(guī)則的命令分別應(yīng)用于前一行,當(dāng)前或者后一行代碼。

// 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

配置

在需要執(zhí)行 SwiftLint 的目錄下添加一個(gè) .swiftlint.yml 文件的方式來(lái)配置 SwiftLint??梢员慌渲玫膮?shù)有:

  • disabled_rules: 關(guān)閉某些默認(rèn)開(kāi)啟的規(guī)則。
  • opt_in_rules: 一些規(guī)則是可選的,添加到這里才會(huì)生效。
  • only_rules: 不可以和 disabled_rules 或者 opt_in_rules 并列。類(lèi)似一個(gè)白名單,只有在這個(gè)列表中的規(guī)則才是開(kāi)啟的。
disabled_rules: # 執(zhí)行時(shí)排除掉的規(guī)則
  - colon
opt_in_rules: # 一些規(guī)則僅僅是可選的
  - empty_count
included: # 執(zhí)行 linting 時(shí)包含的路徑。
  - Source
excluded: # 執(zhí)行 linting 時(shí)忽略的路徑。 優(yōu)先級(jí)比 `included` 更高。
  - Carthage
force_cast: warning # 隱式
force_try:
  severity: warning # 顯式
type_body_length: # 可以通過(guò)一個(gè)數(shù)組同時(shí)進(jìn)行隱式設(shè)置
  - 300 # warning
  - 400 # error
file_length: # 或者也可以同時(shí)進(jìn)行顯式設(shè)置
  warning: 500
  error: 1200

reporter: "xcode" # 報(bào)告類(lèi)型 (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging)

自定義規(guī)則

在配置文件 .swiftlint.yml 里定義基于正則表達(dá)式的自定義規(guī)則:

custom_rules:
  pirates_beat_ninjas: # 規(guī)則標(biāo)識(shí)符
    name: "Pirates Beat Ninjas" # 規(guī)則名稱(chēng),可選
    regex: "([nN]inja)" # 匹配的模式
    match_kinds: # 需要匹配的語(yǔ)法類(lèi)型,可選
      - comment
      - identifier
    message: "Pirates are better than ninjas." # 提示信息,可選
    severity: error # 提示的級(jí)別,可選
  no_hiding_in_strings:
    regex: "([nN]inja)"
    match_kinds: string

輸出大概可能是這個(gè)樣子的:

custom_rules

通過(guò)提供一個(gè)或者多個(gè) match_kinds 的方式來(lái)對(duì)匹配進(jìn)行篩選,它會(huì)將含有不包括在列表中的語(yǔ)法類(lèi)型的匹配排除掉。這里有全部可用的語(yǔ)法類(lèi)型:
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

嵌套配置

SwiftLint 支持通過(guò)嵌套配置文件的方式來(lái)對(duì)代碼分析過(guò)程進(jìn)行更加細(xì)致的控制。

  1. 在你需要的目錄引入 .swiftlint.yml。
  2. 在目錄結(jié)構(gòu)必要的地方引入額外的 .swiftlint.yml 文件。
  3. 每個(gè)文件被檢查時(shí)會(huì)使用在文件所在目錄下的或者父目錄的更深層目錄下的配置文件。否則根配置文件將會(huì)生效。
  4. excluded 和 included 在嵌套結(jié)構(gòu)中會(huì)被忽略。

自動(dòng)更正

SwiftLint 可以自動(dòng)修正某些錯(cuò)誤,磁盤(pán)上的文件會(huì)被一個(gè)修正后的版本覆蓋。
請(qǐng)確保在對(duì)文件執(zhí)行 swiftlint autocorrect 之前有對(duì)它們做過(guò)備份,否則的話(huà)有可能導(dǎo)致重要數(shù)據(jù)的丟失。
因?yàn)樵趫?zhí)行自動(dòng)更正修改某個(gè)文件后很有可能導(dǎo)致之前生成的代碼檢查信息無(wú)效或者不正確,所以當(dāng)在執(zhí)行代碼更正時(shí)標(biāo)準(zhǔn)的檢查是無(wú)法使用的。

SwiftLint rules 原理

SwiftLint rules 的源碼在 Source/SwiftLintFramework/Rules 目錄下,以 OverriddenSuperCallRule(方法需要調(diào)用 super method) 為例解讀源碼,路徑為:/Source/SwiftLintFramework/Rules/Lint/OverriddenSuperCallRule.swift。
| overridden_super_call | yes | no | no | lint | no | warning, excluded: [], included: [“*"] |
主要看 validate(file:kind:dictionary:) 方法,在方法中使用了 configuration、SourceKittenDictionary
configuration: OverriddenSuperCallConfiguration

public struct OverriddenSuperCallConfiguration: RuleConfiguration, Equatable {
    private let defaultIncluded = [
        "addChildViewController(_:)",
        "didReceiveMemoryWarning()",
        "removeFromParentViewController()",
        "viewDidAppear(_:)",
        "viewDidDisappear(_:)",
        "viewDidLoad()",
        "viewWillAppear(_:)",
        "viewWillDisappear(_:)",
        ...
    ]
    var severityConfiguration = SeverityConfiguration(.warning)
    var excluded: [String] = []
    var included: [String] = ["*"]
    public private(set) var resolvedMethodNames: [String]
    init() {
        resolvedMethodNames = defaultIncluded
    }
    public var consoleDescription: String {
        return severityConfiguration.consoleDescription +
            ", excluded: \(excluded)" +
            ", included: \(included)"
    }
    public mutating func apply(configuration: Any) throws {
        guard let configuration = configuration as? [String: Any] else {
            throw ConfigurationError.unknownConfiguration
        }
        if let severityString = configuration["severity"] as? String {
            try severityConfiguration.apply(configuration: severityString)
        }
        if let excluded = [String].array(of: configuration["excluded"]) {
            self.excluded = excluded
        }
        if let included = [String].array(of: configuration["included"]) {
            self.included = included
        }
        resolvedMethodNames = calculateResolvedMethodNames()
    }
    public var severity: ViolationSeverity {
        return severityConfiguration.severity
    }
    private func calculateResolvedMethodNames() -> [String] {
        var names: [String] = []
        if included.contains("*") && !excluded.contains("*") {
            names += defaultIncluded
        }
        names += included.filter({ $0 != "*" })
        names = names.filter { !excluded.contains($0) }
        return names
    }
}
  • defaultIncluded: 默認(rèn)的配置,里面有一些方法名。
  • severityConfiguration: 規(guī)則沖突提示級(jí)別
  • excluded: 需要排除的方法
  • included:需要包含的方法
  • func apply(configuration: Any): 應(yīng)用自定義的配置
  • calculateResolvedMethodNames(): 綜合配置

SourceKittenDictionary 使用 [String: SourceKitRepresentable] 初始化。
[String: SourceKitRepresentable] 是 sourcekitten structure --file SourcekittenDemoViewController.swift 命令生成的 json 對(duì)象。詳看: sourcekitten_structure.json
SourceKittenDictionary

public struct SourceKittenDictionary {
   init(_ value: [String: SourceKitRepresentable]) {…}
…

以 SourcekittenDemoViewController.swift 為例:

class SourcekittenDemoViewController: UIViewController {
    private var testVar: String?
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.lightGray
        testVar = "i am testVar"
    }
}

sourcekitten_structure.json

{
  "key.diagnostic_stage" : "source.diagnostic.stage.swift.parse",
  "key.length" : 414,
  "key.offset" : 0,
  "key.substructure" : [
    {
      "key.accessibility" : "source.lang.swift.accessibility.internal",
      "key.bodylength" : 189,
      "key.bodyoffset" : 223,
      "key.elements" : [
        {
          "key.kind" : "source.lang.swift.structure.elem.typeref",
          "key.length" : 16,
          "key.offset" : 205
        }
      ],
      "key.inheritedtypes" : [
        {
          "key.name" : "UIViewController"
        }
      ],
      "key.kind" : "source.lang.swift.decl.class",
      "key.length" : 246,
      "key.name" : "SourcekittenDemoViewController",
      "key.namelength" : 30,
      "key.nameoffset" : 173,
      "key.offset" : 167,
      "key.substructure" : [
        {
          "key.accessibility" : "source.lang.swift.accessibility.private",
          "key.attributes" : [
            {
              "key.attribute" : "source.decl.attribute.private",
              "key.length" : 7,
              "key.offset" : 228
            }
          ],
          "key.kind" : "source.lang.swift.decl.var.instance",
          "key.length" : 20,
          "key.name" : "testVar",
          "key.namelength" : 7,
          "key.nameoffset" : 240,
          "key.offset" : 236,
          "key.setter_accessibility" : "source.lang.swift.accessibility.private",
          "key.typename" : "String?"
        },
        {
          "key.accessibility" : "source.lang.swift.accessibility.internal",
          "key.attributes" : [
            {
              "key.attribute" : "source.decl.attribute.override",
              "key.length" : 8,
              "key.offset" : 261
            }
          ],
          "key.bodylength" : 120,
          "key.bodyoffset" : 290,
          "key.kind" : "source.lang.swift.decl.function.method.instance",
          "key.length" : 141,
          "key.name" : "viewDidLoad()",
          "key.namelength" : 13,
          "key.nameoffset" : 275,
          "key.offset" : 270,
          "key.substructure" : [
            {
              "key.bodylength" : 0,
              "key.bodyoffset" : 317,
              "key.kind" : "source.lang.swift.expr.call",
              "key.length" : 19,
              "key.name" : "super.viewDidLoad",
              "key.namelength" : 17,
              "key.nameoffset" : 299,
              "key.offset" : 299
            }
          ]
        }
      ]
    }
  ]
}

validate(file:kind:dictionary:)

func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
          dictionary: SourceKittenDictionary) -> [StyleViolation] {
             guard let offset = dictionary.bodyOffset,
                       let name = dictionary.name,
                   kind == .functionMethodInstance,
                       configuration.resolvedMethodNames.contains(name),
                   dictionary.enclosedSwiftAttributes.contains(.override)
       else { return [] }
              let callsToSuper = dictionary.extractCallsToSuper(methodName: name)
              if callsToSuper.isEmpty {
                 …
                 "Method '\(name)' should call to super function"
              } else if callsToSuper.count > 1 {
                 ...
                 "Method '\(name)' should call to super only once"
              }
              return []
}
public struct SourceKittenDictionary {
…
internal func extractCallsToSuper(methodName: String) -> [String] {
    guard let methodNameWithoutArguments = methodName.split(
separator: "(").first else { return [] }
    let superCall = “super.\(methodNameWithoutArguments)"
    return substructure.flatMap { elems -> [String] in
        guard let type = elems.expressionKind,
        let name = elems.name,
        type == .call && superCall == name else {
            return elems.extractCallsToSuper(
methodName: methodName)}
        return [name]}}
}

validate(file:kind:dictionary:) 通過(guò)判斷:

方法 == 含有 .override 屬性的實(shí)例方法 && 
methodName 在配置列表中 &&
Substructure 中表達(dá)式類(lèi)型是 .call && 
Substructure 中方法調(diào)用名稱(chēng) == super.{methodName}

滿(mǎn)足上述條件的方法內(nèi)若沒(méi)有調(diào)用對(duì)應(yīng)的 super 方法,或者
調(diào)用了多次對(duì)應(yīng)的 super 方法,就會(huì)提示規(guī)則沖突。

問(wèn):sourcekitten 是什么?
答:SourceKitten 是基于 Apple 的 SourceKit 封裝的命令行工具,SourceKitten 鏈接并與 sourcekitd.framework 通信以解析 Swift AST,最終提取 Swift 或 ObjC 文件的類(lèi)結(jié)構(gòu)和方法等。
sourcekitten 支持的命令如下:

SUBCOMMANDS:
  complete                Generate code completion options
  doc                     Print Swift or Objective-C docs as JSON
  format                  Format Swift file
  index                   Index Swift file and print as JSON
  module-info          Obtain Swift module information and print as json
  request                 Run a raw SourceKit request
  structure               Print Swift structure information as JSON
  syntax                  Print Swift syntax information as JSON
  version                 Display the current version of SourceKitten

問(wèn):SourceKit 是什么?
答:SourceKit 是一套工具集,使得大多數(shù) Swift 源代碼層面的操作特性得以支持,例如源代碼解析、語(yǔ)法高亮、排版(typesetting)、自動(dòng)補(bǔ)全、跨語(yǔ)言頭文件生成,等等

問(wèn):Swift AST 是什么?
答:AST(Abstract Syntax Tree 抽象語(yǔ)法樹(shù)) 是源代碼的抽象語(yǔ)法結(jié)構(gòu)的樹(shù)狀表示,樹(shù)上的每個(gè)節(jié)點(diǎn)都表示源代碼中的一種結(jié)構(gòu),是 Swift 文件編譯過(guò)程中的產(chǎn)物。

Swift 文件編譯過(guò)程:

Swift 文件編譯過(guò)程

image.png

Swiftc 生成 AST
Swiftc 是 swift 語(yǔ)言的編譯工具,它可以直接把 .swift 文件編譯生成可執(zhí)行文件,也可以產(chǎn)生編譯過(guò)程中某個(gè)中間文件。Swiftc 支持命令如下:

MODES:
  -dump-ast              Parse and type-check input file(s) and dump AST(s)
  -dump-parse            Parse input file(s) and dump AST(s)
  -emit-assembly         Emit assembly file(s) (-S)
  -emit-executable       Emit a linked executable
  -emit-ir               Emit LLVM IR file(s)
  -emit-sibgen           Emit serialized AST + raw SIL file(s)
  -emit-sib              Emit serialized AST + canonical SIL file(s)
  -emit-silgen           Emit raw SIL file(s)
  -emit-sil              Emit canonical SIL file(s)
  -parse                 Parse input file(s)
  -print-ast             Parse and type-check input file(s) and pretty print AST(s)
  -typecheck             Parse and type-check input file(s)
...

swiftc -dump-parse SourcekittenDemoViewController.swift

(source_file "SourcekittenDemoViewController.swift"
  (import_decl range=[SourcekittenDemoViewController.swift:9:1 - line:9:8] 'UIKit')
  (class_decl range=[SourcekittenDemoViewController.swift:11:1 - line:18:1] "SourcekittenDemoViewController" inherits: <null>
    (pattern_binding_decl range=[SourcekittenDemoViewController.swift:12:13 - line:12:32]
      (pattern_typed
        (pattern_named 'testVar')
        (type_optional
          (type_ident
            (component id='String' bind=none)))))
    (var_decl range=[SourcekittenDemoViewController.swift:12:17 - line:12:17] "testVar" type='<null type>' readImpl=stored writeImpl=stored readWriteImpl=stored)
    (func_decl range=[SourcekittenDemoViewController.swift:13:14 - line:17:5] "viewDidLoad()"
      (parameter "self")
      (parameter_list range=[SourcekittenDemoViewController.swift:13:30 - line:13:31])
      (brace_stmt range=[SourcekittenDemoViewController.swift:13:33 - line:17:5]
        (call_expr type='<null>' arg_labels=
          (unresolved_dot_expr type='<null>' field 'viewDidLoad' function_ref=unapplied
            (super_ref_expr type='<null>'))
          (tuple_expr type='()' location=SourcekittenDemoViewController.swift:14:26 range=[SourcekittenDemoViewController.swift:14:26 - line:14:27]))
        (sequence_expr type='<null>'
          (unresolved_dot_expr type='<null>' field 'backgroundColor' function_ref=unapplied
            (unresolved_dot_expr type='<null>' field 'view' function_ref=unapplied
              (declref_expr type='<null>' decl=SourcekittenDemoViewController.(file).SourcekittenDemoViewController.viewDidLoad().self@SourcekittenDemoViewController.swift:13:19 function_ref=unapplied)))
          (assign_expr type='<null>'
            (**NULL EXPRESSION**)
            (**NULL EXPRESSION**))
          (unresolved_dot_expr type='<null>' field 'lightGray' function_ref=unapplied
            (unresolved_decl_ref_expr type='<null>' name=UIColor function_ref=unapplied)))
        (sequence_expr type='<null>'
          (unresolved_decl_ref_expr type='<null>' name=testVar function_ref=unapplied)
          (assign_expr type='<null>'
            (**NULL EXPRESSION**)
            (**NULL EXPRESSION**))
          (string_literal_expr type='<null>' encoding=utf8 value="i am testVar" builtin_initializer=**NULL** initializer=**NULL**))))))

通過(guò)生成的 AST 信息可以看到 import_decl、class_decl、var_decl、func_decl、brace_stmt、call_expr 等及所在的行列數(shù)。sourcekit 可以通過(guò)這些信息實(shí)現(xiàn)語(yǔ)法高亮、排版、自動(dòng)補(bǔ)全、跨語(yǔ)言頭文件生成,等等

提醒 執(zhí)行 swiftc -print-ast SourcekittenDemoViewController.swift 命令時(shí),終端會(huì)報(bào)錯(cuò) SourcekittenDemoViewController.swift:9:8: error: no such module 'UIKit' .
為解決此報(bào)錯(cuò),需要指定 -sdk 和 -target 參數(shù),如下:
swiftc -print-ast SourcekittenDemoViewController.swift -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.5.sdk -target arm64-apple-ios15.5
"15.5" 需要根據(jù)本地支持的版本做修改。

SwiftLint 規(guī)則建議

Swiftlint 規(guī)則沒(méi)有完全符合每個(gè)工程期望的統(tǒng)一規(guī)則?;陧?xiàng)目應(yīng)用上面,我得出一些經(jīng)驗(yàn)教訓(xùn),在此給出一點(diǎn)建議:

  1. 成熟的項(xiàng)目規(guī)則宜松不宜緊。規(guī)則過(guò)多或過(guò)嚴(yán)會(huì)直接導(dǎo)致產(chǎn)生較多警告,改動(dòng)太耗時(shí)間,產(chǎn)生大量提交。萬(wàn)一修改出了錯(cuò)誤,查起來(lái)也會(huì)非常麻煩。
  2. 可糾正代碼 bug 的規(guī)則,建議提示級(jí)別為 error,而非 warning。如此可及時(shí)提醒修正錯(cuò)誤。
    例如: OverriddenSuperCallRule 規(guī)則可用于避免遺漏調(diào)用 super 方法,從而避免工程中對(duì)此方法的 hook 不生效。
    例如: DiscardedNotificationCenterObserverRule 規(guī)則可用于避免因?yàn)槲闯钟?observer,不能釋放內(nèi)存,從而導(dǎo)致內(nèi)存泄露的 bug。
  3. 推薦 swift 寫(xiě)法的規(guī)則建議使用。
    例如:使用 CGPoint(x:y:) 代替 CGPointMake(,)

基于上面三條建議,制定的規(guī)則示例如下:

only_rules:
  - block_based_kvo # Swift 3.2 之后使用新的 KVO API
  - compiler_protocol_init # 不應(yīng)該直接調(diào)用字面量轉(zhuǎn)換的初始化方法
  - control_statement # if while 等判斷條件不要用括號(hào)括起來(lái)
  - custom_rules # 一些自定義規(guī)則
  - discarded_notification_center_observer # 當(dāng)使用 block 注冊(cè)通知中心 observer 的時(shí)候,應(yīng)該存儲(chǔ)函數(shù)返回的 observer, 以便之后的刪除
  - discouraged_optional_boolean # 不建議使用可選布爾值
  - duplicate_imports # 重復(fù)導(dǎo)入
  - duplicate_enum_cases # 枚舉不能設(shè)置兩個(gè)或者以上相同的名字
  - empty_count
  - empty_string # 優(yōu)先使用 isEmpty 判斷,而不是將字符串與空字符串文字進(jìn)行比較
  - empty_parameters # 閉包參數(shù)為空時(shí),建議使用 `() -> ` 代替 `Void ->
  - explicit_init # 避免直接調(diào)用 init 方法
  - fallthrough # switch 語(yǔ)句中不建議使用 fallthrough
  - fatal_error_message # fatalError 必須擁有一個(gè) message
  - file_name_no_space # 文件名不應(yīng)包含任何空格
  - force_cast # 不建議直接強(qiáng)解類(lèi)型
  - force_try # 避免 `try!`
  - force_unwrapping # 避免強(qiáng)制解包
  - identical_operands # 比較兩個(gè)相同的操作數(shù)可能是一個(gè)錯(cuò)誤
  - legacy_cggeometry_functions # 避免使用 C 風(fēng)格的 CG 遺留函數(shù),使用 struct extension
  - legacy_constructor # 使用 swift 提供的 struct 構(gòu)造函數(shù), 避免使用遺留的構(gòu)造函數(shù)比如 CGPointMake(10, 10)
  - legacy_nsgeometry_functions # 避免使用 C 風(fēng)格的 NS 遺留函數(shù),使用 struct extension
  - literal_expression_end_indentation # 數(shù)組和字典文字的結(jié)尾應(yīng)與開(kāi)始它的行具有相同的縮進(jìn)
  - lower_acl_than_parent # 確保定義的訪問(wèn)控制級(jí)別低于其父級(jí)
  - mark # 正確使用 mark 的格式 `// MARK: - message`
  - multiline_parameters # 函數(shù)和方法參數(shù)應(yīng)該在同一行上,或者每行一個(gè)
  - no_extension_access_modifier # 在 extension 擴(kuò)展前面,不建議使用 (fileprivate,public) 等修飾符
  - redundant_objc_attribute # Objective-C 屬性(@objc)在聲明中是多余的
  - redundant_optional_initialization # 不需要寫(xiě)默認(rèn)值為 nil
  - redundant_string_enum_value # 字符串類(lèi)型枚舉,會(huì)有默認(rèn) string 值,與名字相同,不要再次設(shè)置
  - redundant_void_return # 在不必要的時(shí)候, 不需要寫(xiě) ->() and -> Void
  - return_arrow_whitespace # 函數(shù)定義返回的 ->  前后有空格, 不換行
  - switch_case_alignment # Case 語(yǔ)句應(yīng)與其封閉的 switch 語(yǔ)句垂直對(duì)齊,如果沒(méi)有其他配置,則縮進(jìn)
  - trailing_semicolon # 行末尾不加分號(hào)
  - type_name # 類(lèi)型名字限制規(guī)則(類(lèi)型名稱(chēng)應(yīng)僅包含字母數(shù)字字符,以大寫(xiě)字符開(kāi)頭,長(zhǎng)度在 3 到 40 個(gè)字符之間)
  - unneeded_break_in_switch # 在 switch-case 語(yǔ)句中, 有方法調(diào)用或操作時(shí),避免使用 break 語(yǔ)句
  - unowned_variable_capture # 最好將引用捕獲為弱引用以避免潛在的崩潰
  - void_return # 使用 `-> Void` 代替 `-> ()
  - weak_delegate # delegate 應(yīng)該被設(shè)置為 weak

excluded: # paths to ignore during linting. Takes precedence over `included`.
  - Tests
  - Example
  - Resources

force_cast:
  severity: warning
  
force_try:
  severity: warning

no_extension_access_modifier:
  severity: warning
  
empty_count:
  severity: warning

type_name:
  min_length: 1
  max_length: 60

overridden_super_call:
  severity: error

prohibited_super_call:
  severity: error
  
discarded_notification_center_observer:
  severity: error

weak_delegate:
  severity: error

unowned_variable_capture:
  severity: error

reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown)

custom_rules:
  ab_test_recovery:
    name: "AB Test Recovery"
    regex: '^ *//+[ \S]* ABTestHelper\.+'
    match_kinds:
      - comment
      - doccomment
    message: "AB Test Recovery. 調(diào)試期間注釋掉的 ab test,上線(xiàn)前記得恢復(fù)"
    severity: warning
  url_Check:
    name: "URL Check"
    regex: '(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]+[()()]'
    match_kinds: string
    message: "URL 中發(fā)現(xiàn)有 '(' 或 ')' 或 '(' 或 ')' ,請(qǐng)注意確認(rèn)正確性"
    severity: warning

附錄

1. swiftlint rules

identifier opt-in correctable enabled in your config kind analyzer configuration comment
anyobject_protocol yes yes no lint no warning 對(duì)于純類(lèi)協(xié)議,建議AnyObject 不推薦 class
array_init yes no no lint no warning 推薦使用 Array(seq) 不推薦語(yǔ)法: seq.map { $0 } 將序列轉(zhuǎn)換為Array
attributes yes no no style no warning, always_on_same_line: ["@IBAction", "@NSManaged"], alwa... 屬性應(yīng)該在函數(shù)和類(lèi)型中自己的行上,與變量和 imports 在同一行上
balanced_xctest_lifecycle yes no no lint no warning -
block_based_kvo no no yes idiomatic no warning 屬性和下標(biāo)中的Getter和setter應(yīng)該保持一致的順序
capture_variable yes no no lint yes warning -
class_delegate_protocol no no yes lint no warning delegate protocol 應(yīng)該被設(shè)定為 class-only,才能被弱引用
closing_brace no yes yes style no warning 小括號(hào)內(nèi)包含函數(shù)(大括號(hào))的時(shí)候,之間沒(méi)有空格
closure_body_length yes no no metrics no warning: 20, error: 100 封閉體不應(yīng)跨越太多行
closure_end_indentation yes yes no style no warning 閉包前后縮進(jìn)應(yīng)相同
closure_parameter_position no no yes style no warning 閉包參數(shù)位置, 閉包參數(shù)應(yīng)該 { 左邊在同一行
closure_spacing yes yes no style no warning 閉包表達(dá)式在每個(gè)大括號(hào) { } 內(nèi)前后應(yīng)有一個(gè)空格
collection_alignment yes no no style no warning, align_colons: false 集合文字中的所有元素應(yīng)垂直對(duì)齊
colon no yes yes style no warning, flexible_right_spacing: false, apply_to_dictionaries: ... 冒號(hào)左邊沒(méi)有空格, 右邊有且只有一個(gè)空格
comma no yes yes style no warning 逗號(hào)左邊沒(méi)有空格, 右邊有空格
comment_spacing no yes yes lint no warning -
compiler_protocol_init no no yes lint no warning 不應(yīng)該直接調(diào)用字面量轉(zhuǎn)換的初始化方法,諸如編譯器協(xié)議中聲明的初始化程序ExpressibleByArrayLiteral不應(yīng)直接調(diào)用
computed_accessors_order no no yes style no warning, order: get_set -
conditional_returns_on_newline yes no no style no warning, if_only: false 條件語(yǔ)句與結(jié)果不建議寫(xiě)在一行 ,例如:guard true else { return } ;if true { return "YES" } else { return "NO" } 會(huì)有 warning提示
contains_over_first_not_nil yes no no performance no warning 推薦使用 contains,避免使用 first(where:) != nil 與 firstIndex(where:) != nil
control_statement no yes yes style no warning if while 等判斷條件不要用括號(hào) 括起來(lái),另外注意條件出的空格
convenience_type yes no no idiomatic no warning 用于檢測(cè)靜態(tài)成員的類(lèi)型應(yīng)實(shí)現(xiàn)為無(wú)大小寫(xiě)的枚舉,以避免實(shí)例化
custom_rules no no no style no user-defined 通過(guò)提供正則表達(dá)式字符串來(lái)創(chuàng)建自定義規(guī)則
cyclomatic_complexity no no yes metrics no warning: 10, error: 20, ignores_case_statements: false 代碼復(fù)雜度,默認(rèn)為10,循環(huán)復(fù)雜度。函數(shù)體的復(fù)雜度的限制,這個(gè)屬性主要約束條件句、循環(huán)句中的循環(huán)嵌套問(wèn)題, 當(dāng)嵌套太多的循環(huán)時(shí),則會(huì)觸發(fā)swiftlint中的warning和error,當(dāng)達(dá)到10個(gè)循環(huán)嵌套時(shí)就會(huì)報(bào)warning,達(dá)到20個(gè)循環(huán)嵌套時(shí)就會(huì)報(bào)error,強(qiáng)烈推薦這個(gè)屬性。嵌套太多,可讀性差
deployment_target no no yes lint no warning, iOS_deployment_target: 7.0, macOS_deployment_target: 1... -
discarded_notification_center_observer yes no no lint no warning 當(dāng)使用 block 注冊(cè)通知中心 observer 的時(shí)候, 應(yīng)該存儲(chǔ)函數(shù)返回的 observer, 以便之后的刪除
discouraged_assert yes no no idiomatic no warning -
discouraged_direct_init no no yes lint no warning, types: ["Bundle", "Bundle.init", "UIDevice", "UIDevice... 不鼓勵(lì)直接初始化并聲明的類(lèi)型 warning:types: ["Bundle", "Bundle.init", "UIDevice", "UIDevice.init"]
discouraged_object_literal yes no no idiomatic no warning, image_literal: true, color_literal: true 避免使用圖片和顏色的字面量(Ltiteral),盡量使用初始化的方式
discouraged_optional_boolean yes no no idiomatic no warning 推薦使用非可選的bool值
discouraged_optional_collection yes no no idiomatic no warning 優(yōu)先選擇空集合而不是可選集合
duplicate_enum_cases no no yes lint no error 枚舉不能設(shè)置兩個(gè)或者以上相同的名字
duplicate_imports no no yes idiomatic no warning 重復(fù)導(dǎo)入
dynamic_inline no no yes lint no error 避免同時(shí)使用'dynamic'和'@inline(__ always)'
empty_count yes no no performance no error, only_after_dot: false 建議使用isEmpty判斷,而不是使用count==0判斷
empty_enum_arguments no yes yes style no warning 如果將枚舉與關(guān)聯(lián)的類(lèi)型匹配(如果不使用),則可以忽略參數(shù)
empty_parameters no yes yes style no warning 使用 () -> 代替 `Void ->
empty_parentheses_with_trailing_closure no yes yes style no warning 尾閉包避免空參數(shù)括號(hào)
empty_string yes no no performance no warning 優(yōu)先使用isEmpty判斷,而不是將字符串與空字符串文字進(jìn)行比較
empty_xctest_method yes no no lint no warning 應(yīng)避免使用空的XCTest方法
enum_case_associated_values_count yes no no metrics no warning: 5, error: 6 枚舉情況下的關(guān)聯(lián)值數(shù)量應(yīng)少
expiring_todo yes no no lint no (approaching_expiry_severity) warning, (reached_or_passed_expir... TODO和FIXME應(yīng)該在其到期日之前解決
explicit_acl yes no no idiomatic no warning 所有聲明都應(yīng)明確指定訪問(wèn)控制級(jí)別關(guān)鍵字
explicit_enum_raw_value yes no no idiomatic no warning 枚舉應(yīng)設(shè)置默認(rèn)值
explicit_init yes yes no idiomatic no warning 避免直接調(diào)用 init 方法
explicit_self yes yes no style yes warning 實(shí)例變量和函數(shù)應(yīng)使用“self”顯式訪問(wèn)
explicit_top_level_acl yes no no idiomatic no warning 頂級(jí)聲明應(yīng)明確指定訪問(wèn)控制級(jí)別關(guān)鍵字
explicit_type_interface yes no no idiomatic no warning, excluded: [], allow_redundancy: false 需要跑明確參數(shù)的類(lèi)型定義
extension_access_modifier yes no no idiomatic no warning 優(yōu)先使用擴(kuò)展名訪問(wèn)修飾符
fallthrough yes no no idiomatic no warning 避免在 case語(yǔ)句中使用 fallthrough
fatal_error_message yes no no idiomatic no warning 必須擁有一個(gè) message
file_header yes no no style no warning, required_string: None, required_pattern: None, forbidd... 標(biāo)頭注釋?xiě)?yīng)與項(xiàng)目模式一致
file_length no no yes metrics no warning: 400, error: 1000, ignore_comment_only_lines: false 文件長(zhǎng)度限制
file_name yes no no idiomatic no (severity) warning, excluded: ["LinuxMain.swift", "main.swift"]... 文件名應(yīng)與文件中聲明的類(lèi)型或擴(kuò)展名匹配(如果有
file_types_order yes no no style no warning, order: [[SwiftLintFramework.FileType.supportingType], ... 指定如何排序文件中的類(lèi)型
first_where yes no no performance no warning 使用 .first(where:) 代替 .filter { }.first
flatmap_over_map_reduce yes no no performance no warning 推薦使用 flatMap,避免使用 map 的 reduce([], +)
for_where no no yes idiomatic no warning 使用 for where 代替 簡(jiǎn)單的 for { if }
force_cast no no yes idiomatic no error 避免強(qiáng)制的類(lèi)型轉(zhuǎn)化,這里表示強(qiáng)解類(lèi)型警告 as! Int
force_try no no yes idiomatic no error 對(duì)會(huì)拋出異常(throws)的方法,不建議try,強(qiáng)解, 避免 try!
force_unwrapping yes no no idiomatic no warning 避免強(qiáng)制解包
function_body_length no no yes metrics no warning: 40, error: 100 函數(shù)體長(zhǎng)度 默認(rèn)超過(guò)40行warning,超過(guò)100行直接報(bào)錯(cuò)。推薦使用
function_default_parameter_at_end yes no no idiomatic no warning 方法中參數(shù)列表,應(yīng)將帶有默認(rèn)值的參數(shù)放在最后面
function_parameter_count no no yes metrics no warning: 5, error: 8ignores_default_parameters: true 函數(shù)參數(shù)個(gè)數(shù)
generic_type_name no no yes idiomatic no (min_length) w/e: 1/0, (max_length) w/e: 20/1000, excluded: [],... 類(lèi)型命名規(guī)則限制,以大寫(xiě)字母開(kāi)頭,且長(zhǎng)度在1到20個(gè)字符之間
ibinspectable_in_extension yes no no lint no warning 擴(kuò)展不應(yīng)添加@IBInspectable屬性
identical_operands yes no no lint no warning 比較兩個(gè)相同的操作數(shù)可能是一個(gè)錯(cuò)誤
identifier_name no no yes style no (min_length) w/e: 3/2, (max_length) w/e: 40/60, excluded: [], a... 參數(shù)變量命名規(guī)則
implicit_getter no no yes style no warning 參數(shù)不應(yīng)該有 getter 方法
implicit_return yes yes no style no warning, included: [getter, closure, function] 在閉包,函數(shù)和getter中更喜歡隱式返回
implicitly_unwrapped_optional yes no no idiomatic no warning, mode: allExceptIBOutlets 避免隱式解析可選類(lèi)型的使用 / 避免隱式解包(定義 ! 類(lèi)型)
inclusive_language no no yes style no warning, additional_terms: [], override_terms: [], override_all...
inert_defer no no yes lint no warning 如果defer在其父范圍的末尾,則無(wú)論如何它都會(huì)被執(zhí)行
is_disjoint no no yes idiomatic no warning 優(yōu)先:Set.isDisjoint(with:) 不建議:Set.intersection(_:).isEmpty
joined_default_parameter yes yes no idiomatic no warning 不推薦顯式使用默認(rèn)分隔符
large_tuple no no yes metrics no warning: 2, error: 3 元祖成員 元組沖突:元組應(yīng)該最多有2個(gè)成員,多余兩個(gè)會(huì)報(bào)錯(cuò)
last_where yes no no performance no warning 推薦在集合中使用:.last(where:) 不推薦使用: .filter { }.last
leading_whitespace no yes yes style no warning 文件末尾不應(yīng)該存在空格符
legacy_cggeometry_functions no yes yes idiomatic no warning 避免使用 C 風(fēng)格 的 CG 遺留函數(shù), 使用 struct extension
legacy_constant no yes yes idiomatic no warning 避免使用 遺留的全局常量, 使用 struct 內(nèi)定義的 常量
legacy_constructor no yes yes idiomatic no warning 使用 swift 提供的 struct 構(gòu)造函數(shù), 避免使用 遺留的構(gòu)造函數(shù) 比如 CGPointMake(10, 10)
legacy_hashing no no yes idiomatic no warning hash(into:)優(yōu)先使用函數(shù)而不是覆蓋hashValue
legacy_multiple yes no no idiomatic no warning 推薦使用isMultiple(of:)函數(shù),不推薦使用余數(shù)運(yùn)算符(%)
legacy_nsgeometry_functions no yes yes idiomatic no warning 避免使用 C 風(fēng)格 的 NS 遺留函數(shù), 使用 struct extension
legacy_objc_type yes no no idiomatic no warning -
legacy_random yes no no idiomatic no warning 隨機(jī)函數(shù) 優(yōu)先使用type.random(in :),不建議使用舊版函數(shù)
let_var_whitespace yes no no style no warning let和var應(yīng)該用空白行與其他語(yǔ)句分開(kāi)
line_length no no yes metrics no warning: 120, error: 200, ignores urls: false, ignores function... 行的字符長(zhǎng)度,官方的規(guī)定是超過(guò)120字符就給 warning
literal_expression_end_indentation yes yes no style no warning 數(shù)組和字典文字的結(jié)尾應(yīng)與開(kāi)始它的行具有相同的縮進(jìn)
lower_acl_than_parent yes no no lint no warning 確保定義的訪問(wèn)控制級(jí)別低于其父級(jí)
mark no yes yes lint no warning 正確使用 mark 的格式 // MARK: - message
missing_docs yes no no lint no warning: open, public 聲明應(yīng)記錄在案
modifier_order yes yes no style no warning, preferred_modifier_order: [override, acl, setterACL, d... 修飾符順序應(yīng)一致
multiline_arguments yes no no style no warning, first_argument_location: any_line, only_enforce_after_... 參數(shù)應(yīng)該在同一行,或者每行一個(gè)
multiline_arguments_brackets yes no no style no warning 多行參數(shù)應(yīng)在其新行中包含方括號(hào) []
multiline_function_chains yes no no style no warning 鏈接的函數(shù)調(diào)用應(yīng)該在同一行上,或者每行一個(gè)
multiline_literal_brackets yes no no style no warning 多行文字應(yīng)在其新行中包含方括號(hào) []
multiline_parameters yes no no style no warning, allowsSingleLine: true 函數(shù)和方法參數(shù)應(yīng)該在同一行上,或者每行一個(gè)
multiline_parameters_brackets yes no no style no warning 多行參數(shù)應(yīng)在其新行中包含方括號(hào)
multiple_closures_with_trailing_closure no no yes style no warning 傳遞多個(gè)閉包參數(shù)時(shí),不應(yīng)使用結(jié)尾的閉包語(yǔ)法
nesting no no yes metrics no (type_level) w: 1, (function_level) w: 2, (check_nesting_in_clo... 類(lèi)型定義嵌套不要超過(guò)1層 , 聲明嵌套不要超過(guò)5層
nimble_operator yes yes no idiomatic no warning 避免 expect 一個(gè)確定的判斷
no_extension_access_modifier yes no no idiomatic no error 禁止使用擴(kuò)展訪問(wèn)修飾符
no_fallthrough_only no no yes idiomatic no warning 僅當(dāng)case包含至少一個(gè)其他語(yǔ)句時(shí),才能使用穿透
no_grouping_extension yes no no idiomatic no warning 擴(kuò)展名不應(yīng)用于對(duì)同一源文件中的代碼進(jìn)行分組
no_space_in_method_call no yes yes style no warning 不要在方法名稱(chēng)和括號(hào)之間添加空格
notification_center_detachment no no yes lint no warning NotificationCenter.default.removeObserver 只在 deinit 中被調(diào)用
nslocalizedstring_key yes no no lint no warning 應(yīng)將靜態(tài)字符串用作NSLocalizedString中的鍵
nslocalizedstring_require_bundle yes no no lint no warning 調(diào)用NSLocalizedString應(yīng)該指定包含字符串文件的捆綁軟件
nsobject_prefer_isequal no no yes lint no warning NSObject子類(lèi)應(yīng)實(shí)現(xiàn)isEqual而不是==
number_separator yes yes no style no warning, minimum_length: 0 使用 _ 分割大數(shù), 讓數(shù)字更清晰
object_literal yes no no idiomatic no warning, image_literal: true, color_literal: true 避免 image and color 使用字面量初始化, 需要把相關(guān)圖片名,顏色RGB 等參數(shù)定義為 enum struct 或者常量
opening_brace no yes yes style no warning, allowMultilineFunc: false 右括號(hào)之前應(yīng)有一個(gè)空格,并與聲明在同一行
operator_usage_whitespace yes yes no style no warning, lines_look_around: 2, skip_aligned_constants: true 操作符需要使用一個(gè)空格間隔
operator_whitespace no no yes style no warning 當(dāng)定義空格操作符的時(shí)候,被定義的名字或類(lèi)型兩邊應(yīng)該各有一個(gè)單行空格操作符
optional_enum_case_matching yes yes no style no warning 將枚舉大小寫(xiě)與不帶'?'的可選枚舉匹配 在Swift 5.1及更高版本中受支持
orphaned_doc_comment no no yes lint no warning 注釋要寫(xiě)在聲明中
overridden_super_call yes no no lint no warning, excluded: [], included: ["*"] 方法需要調(diào)用 super method
override_in_extension yes no no lint no warning 擴(kuò)展不應(yīng)覆蓋聲明
pattern_matching_keywords yes no no idiomatic no warning 通過(guò)將關(guān)鍵字移出元組來(lái)組合多個(gè)模式匹配綁定
prefer_nimble yes no no idiomatic no warning
prefer_self_type_over_type_of_self yes yes no style no warning 訪問(wèn)屬性或調(diào)用方法時(shí),最好將“自類(lèi)型”設(shè)置為(of:self)
prefixed_toplevel_constant yes no no style no warning, only_private: false 頂級(jí)常量的前綴應(yīng)為k
private_action yes no no lint no warning IBActions應(yīng)該是私有的
private_outlet yes no no lint no warning, allow_private_set: false IBOutlets 應(yīng)該設(shè)置為 private, 來(lái)避免泄露
private_over_fileprivate no yes yes idiomatic no warning, validate_extensions: false 推薦:private 不建議:fileprivate
private_subject yes no no lint no warning -
private_unit_test no no yes lint no warning: XCTestCase 單元測(cè)試方法 不能設(shè)置為 private
prohibited_interface_builder yes no no lint no warning 禁止用interface Builder 創(chuàng)建視圖
prohibited_super_call yes no no lint no warning, excluded: [[]], included: [["*"]] 某些特殊的 override 方法, 禁止調(diào)用 super method excluded: [[]], included: [["*"]]
protocol_property_accessors_order no yes yes style no warning 在協(xié)議中聲明屬性 要按順序先寫(xiě) get set方法
quick_discouraged_call yes no no lint no warning 不鼓勵(lì)在“describe”和/或“context” 框中進(jìn)行調(diào)用
quick_discouraged_focused_test yes no no lint no warning 不鼓勵(lì)重點(diǎn)測(cè)試。專(zhuān)注于此測(cè)試時(shí),其他測(cè)試將不會(huì)運(yùn)行
quick_discouraged_pending_test yes no no lint no warning 不推薦:未開(kāi)始的測(cè)試。標(biāo)記為待定時(shí),該測(cè)試不會(huì)運(yùn)行
raw_value_for_camel_cased_codable_enum yes no no lint no warning 設(shè)置枚舉建議設(shè)置默認(rèn)值
reduce_boolean no no yes performance no warning 優(yōu)先使用.allSatisfy()或.contains() 不建議使用:reduce(true)或reduce(false)
reduce_into yes no no performance no warning 對(duì)于 copy-on-write 類(lèi)型,推薦使用 reduce(into::) 不建議使用 reduce(:_:)
redundant_discardable_let no yes yes style no warning 使用 _ = foo() 代替 let _ = foo()
redundant_nil_coalescing yes yes no idiomatic no warning #避免使用 object ?? nil 僅當(dāng)lhs為nil時(shí)才評(píng)估nil合并運(yùn)算符,而n為rhs則合并nil合并運(yùn)算符
redundant_objc_attribute no yes yes idiomatic no warning Objective-C屬性(@objc)在聲明中是多余的
redundant_optional_initialization no yes yes idiomatic no warning 用nil初始化可選變量是多余的。 # 默認(rèn)值賦值為nil 不需要寫(xiě)默認(rèn)值為 nil
redundant_set_access_control no no yes idiomatic no warning 如果屬性設(shè)置程序訪問(wèn)級(jí)別與變量訪問(wèn)級(jí)別相同,則不應(yīng)明確
redundant_string_enum_value no no yes idiomatic no warning 在定義字符串枚舉的時(shí)候, 當(dāng)字符串枚舉值等于枚舉名稱(chēng)時(shí),可以不用賦值
redundant_type_annotation yes yes no idiomatic no warning 變量不應(yīng)具有冗余類(lèi)型注釋 建議 var url = URL() 不建議 var url : URL = URL()
redundant_void_return no yes yes idiomatic no warning 在函數(shù)聲明中返回Void是多余的。#在不必要的時(shí)候, 不需要寫(xiě) ->() and -> Void
required_deinit yes no no lint no warning 類(lèi)應(yīng)具有顯式的deinit方法
required_enum_case yes no no lint no No protocols configured. In config add 'required_enum_case' to... 符合指定協(xié)議的枚舉必須實(shí)現(xiàn)特定情況
return_arrow_whitespace no yes yes style no warning 前后要有空格,函數(shù)定義返回的 -> 前后有空格, 不換行
shorthand_operator no no yes style no error 使用+= , -=, *=, /= 代替 a = a + 1
single_test_class yes no no style no warning 測(cè)試文件應(yīng)只包含一個(gè)QuickSpec或XCTestCase類(lèi)
sorted_first_last yes no no performance no warning 優(yōu)先使用min()或max() 不建議使用 sorted().first或sorted().last
sorted_imports yes yes no style no warning Imports 應(yīng)排序
statement_position no yes yes style no (statement_mode) default, (severity) warning 這里主要指的是 else 和 catch 前面要加一個(gè)空格, 也不能大于1個(gè)空格
static_operator yes no no idiomatic no warning 應(yīng)該將運(yùn)算符聲明為靜態(tài)函數(shù),而不是自由函數(shù)
strict_fileprivate yes no no idiomatic no warning fileprivate 應(yīng)該避免
strong_iboutlet yes yes no lint no warning @IBOutlets不應(yīng)被聲明為weak 應(yīng)該為 strong
superfluous_disable_command no no yes lint no warning 當(dāng)禁用規(guī)則不會(huì)在禁用區(qū)域觸發(fā)違規(guī)時(shí),SwiftLint的“禁用”命令是多余的。如果要記錄命令,請(qǐng)使用“-”
switch_case_alignment no no yes style no warning, indented_cases: false Case語(yǔ)句應(yīng)與其封閉的switch語(yǔ)句垂直對(duì)齊,如果沒(méi)有其他配置,則縮進(jìn)
switch_case_on_newline yes no no style no warning switch 的 case 需要新啟一行
syntactic_sugar no yes yes idiomatic no warning 語(yǔ)法糖[Int] 代替Array / 例:要使用 [] ? 等數(shù)組字典可選項(xiàng)的語(yǔ)法糖
test_case_accessibility yes yes no lint no warning, allowed_prefixes: [[]] -
todo no no yes lint no warning 避免 TODOs and FIXMEs 標(biāo)識(shí)
toggle_bool yes yes no idiomatic no warning 不讓使用 A = !A 建議使用 A.toggle()
trailing_closure yes no no style no warning, only_single_muted_parameter: false 盡可能使用尾隨閉包語(yǔ)法
trailing_comma no yes yes style no warning, mandatory_comma: false 數(shù)組末尾不要加空格
trailing_newline no yes yes style no warning 末尾空行,文件末尾應(yīng)該有一個(gè)空行
trailing_semicolon no yes yes idiomatic no warning 行末尾不加分號(hào)
trailing_whitespace no yes yes style no warning, ignores_empty_lines: false, ignores_comments: true 每一個(gè)空行不能有空格
type_body_length no no yes metrics no warning: 200, error: 350 類(lèi)型體長(zhǎng)度不應(yīng)該跨越太多行,超過(guò)200行給warning,超過(guò)350行給error
type_contents_order yes no no style no warning, order: [[SwiftLintFramework.TypeContent.case], [SwiftL... 指定類(lèi)型內(nèi)子類(lèi)型,屬性,方法及更多內(nèi)容的順序
type_name no no yes idiomatic no (min_length) w/e: 3/0, (max_length) w/e: 40/1000, excluded: [],... 類(lèi)型名字限制規(guī)則,類(lèi)型名稱(chēng)只能包含字母數(shù)字字符,以大寫(xiě)字母開(kāi)頭,并且長(zhǎng)度在3到40個(gè)字符之間
unavailable_function yes no no idiomatic no warning 未實(shí)現(xiàn)的功能應(yīng)標(biāo)記為不可用
unneeded_break_in_switch no no yes idiomatic no warning 在switch-case語(yǔ)句中, 有方法調(diào)用或操作時(shí),避免使用break語(yǔ)句
unneeded_parentheses_in_closure_argument yes yes no style no warning 聲明閉包參數(shù)時(shí),不需要括號(hào)
unowned_variable_capture yes no no lint no warning 最好將引用捕獲為弱引用以避免潛在的崩潰
untyped_error_in_catch yes yes no idiomatic no warning 沒(méi)有類(lèi)型轉(zhuǎn)換,catch語(yǔ)句不應(yīng)聲明錯(cuò)誤變量
unused_capture_list no no yes lint no warning 閉包中沒(méi)有被使用的參數(shù)應(yīng)該刪除
unused_closure_parameter no yes yes lint no warning 函數(shù)的參數(shù)必須被使用
unused_control_flow_label no yes yes lint no warning 未使用的控制流標(biāo)簽應(yīng)被刪除
unused_declaration yes no no lint yes severity: error, include_public_and_open: false, related_usrs_t... 在所有被刪除的文件中,聲明至少應(yīng)被引用一次
unused_enumerated no no yes idiomatic no warning 默認(rèn)-當(dāng)參數(shù)沒(méi)有被全部使用的時(shí)候, 不要使用容器的 enumerated 方法
unused_import yes yes no lint yes severity: warning, require_explicit_imports: false, allowed_tra... import 的文件要被使用
unused_optional_binding no no yes style no warning, ignore_optional_try: false 在使用if判斷某變量是否為nil的時(shí)候, 不建議使用下劃線(xiàn)(_) 必須使用定義的 optional binding
unused_setter_value no no yes lint no warning 不使用設(shè)定值
valid_ibinspectable no no yes lint no warning 默認(rèn)-IBInspectable 必須是可變參數(shù)
vertical_parameter_alignment no no yes style no warning 函數(shù)參數(shù)分為多行書(shū)寫(xiě)的時(shí)候, 頭部(小括號(hào)后面一位)必須對(duì)齊 函數(shù)參數(shù)分為多行書(shū)寫(xiě)的時(shí)候, 頭部(小括號(hào)后面一位)必須對(duì)齊
vertical_parameter_alignment_on_call yes no no style no warning 如果函數(shù)參數(shù)在方法調(diào)用中位于多行中,則應(yīng)垂直對(duì)齊
vertical_whitespace no yes yes style no warning, max_empty_lines: 1 垂直方向上的空格行,限制為一行(注釋除外) 不能有連續(xù)多個(gè)空行
vertical_whitespace_between_cases yes yes no style no warning 在 switch cases 之間包括一條空行
vertical_whitespace_closing_braces yes yes no style no N/A 在關(guān)閉大括號(hào)之前,請(qǐng)勿包括垂直空格(空行)
vertical_whitespace_opening_braces yes yes no style no N/A 打開(kāi)花括號(hào)后,請(qǐng)勿包括垂直空格(空行)
void_return no yes yes style no warning -
weak_delegate no yes yes lint no warning 代理要設(shè)置為弱引用
xct_specific_matcher yes no no idiomatic no warning 優(yōu)先使用特定的XCTest匹配器,XCTAssertEqual而不是XCTAssertNotEqual
xctfail_message no no yes idiomatic no warning XCTFail調(diào)用應(yīng)包括斷言的描述,描述不能為空
yoda_condition yes no no lint no warning 變量應(yīng)位于比較運(yùn)算符的左側(cè),常數(shù)應(yīng)位于右側(cè)

參考文獻(xiàn)

  1. SwiftLint
  2. SwiftLint 的理解和使用
  3. rule-directory
  4. AST
  5. LLVM優(yōu)點(diǎn)
  6. How a Swift file is compiled
  7. SwiftSyntax詳解
  8. 使用“swiftc”命令編譯swift文件時(shí)如何導(dǎo)入像“UIKit”這樣的模塊?
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容