Swiftの文法チェックツール「SwiftLint」を導入してみた

SwiftLintは、GitHubが公開している[Swift Style Guide](https://github.com/github/swift-style-guide)をもとにして、Swiftの文法をチェックしてくれるツールです。

## SwiftLintをインストール

SwiftLintの本体はこちら。

[GitHub - realm/SwiftLint: A tool to enforce Swift style and conventions.](https://github.com/realm/SwiftLint)

HomeBrewとCocoaPodsどちらでもインストールができますが、今回はCocoaPodsでインストールします。

``` Podfile
pod 'SwiftLint'
```

## ビルドするときにチェックをする

Xcodeでビルドするときに、自動的にチェックを実行するようにします。

<img width="1399" alt="001.png" src="https://qiita-image-store.s3.amazonaws.com/0/38924/87648117-4684-0144-e6f0-7b482fd933dd.png">

まず、プロジェクトのTARGETSのBuild PhasesにNew Run Script Phaseを追加します。

<img width="1512" alt="002.png" src="https://qiita-image-store.s3.amazonaws.com/0/38924/e14863a3-b296-d906-6aff-bbbf6fd4bac9.png">

CocoaPodsでインストールした場合は、つぎのスクリプトをShellのところに入力します。

```
"${PODS_ROOT}/SwiftLint/swiftlint"
```

## ビルドしてみる

それでは、ためしにビルドしてみます。

<img width="1512" alt="003.png" src="https://qiita-image-store.s3.amazonaws.com/0/38924/6813ef3e-d53b-8dff-7828-8bac7b100ab3.png">

さっそくエラーがたくさんでました。

> Line Length Violation: Line should be 120 characters or less: currently 285 characters (line_length)

> Trailing Newline Violation: Files should have a single trailing newline. (trailing_newline)

1行の文字数を120字以内にしましょう、とか、末尾の行は1行にしましょうといった内容です。
なかなか厳しいですね。

## チェックするルールを調整する方法

デフォルトの設定ですと、Xcodeが自動的に生成したコードにも警告やエラーが発生していまします。このままでは不便なので、チェックするルールを調整します。
また、ライブラリーは基本的にはソースコードを触らないので、Podsディレクトリをチェック対象外に設定します。

チェックするルールを変更する場合は、 .swiftlint.yml を設定します。

プロジェクトのルートディレクトリに .swiftlint.yml ファイルを追加します。そして、.swiftlint.yml ファイルにルールを書くと、オリジナルのルールでチェックしてくれます。

こちらは、標準の .swiftlint.yml ファイルのサンプルです。

``` .swiftlint.yml
disabled_rules: # rule identifiers to exclude from running
  - colon
  - comma
  - control_statement
opt_in_rules: # some rules are only opt-in
  - empty_count
  # Find all the available rules by running:
  # swiftlint rules
included: # paths to include during linting. `--path` is ignored if present.
  - Source
excluded: # paths to ignore during linting. Takes precedence over `included`.
  - Carthage
  - Pods
  - Source/ExcludedFolder
  - Source/ExcludedFile.swift
  - Source/*/ExcludedFile.swift # Exclude files with a wildcard
analyzer_rules: # Rules run by `swiftlint analyze` (experimental)
  - explicit_self
```

- disabled_rules

デフォルトで有効となっているルールのなかから、無効にしたいルールを書きます。

- opt_in_rules

デフォルトで無効となっているルールのなかから、有効にしたいルールを書きます。

- included

チェックしたいファイルのあるパスを書きます。通常はプロジェクト名を書きます。

- excluded

チェックから外すパスを書きます。CocoaPodsはここに書きます。

## ルールを設定しよう

たとえば、 .swiftlint.yml を、つぎのように設定してみます。

``` .swiftlint.yml
disabled_rules:
- trailing_newline
- vertical_whitespace

excluded:
- Pods/
- Podfile
- Podfile.lock

# 1行の文字数が200超えると警告、300超えるとエラーにする
line_length:
- 200 # warning
- 300 # error
```

ルール名は、警告やエラーの最後に表示されるので、それをコピーします。

> Trailing Newline Violation: Files should have a single trailing newline. (trailing_newline)

また、ルールのつぎの行に書くと警告、さらにつぎの行に書くとエラーというような設定もできます。

```
line_length:
- 200 # warning
- 300 # error
```

<img width="956" alt="004.png" src="https://qiita-image-store.s3.amazonaws.com/0/38924/effa9591-d6b4-cd0f-1ce8-fd89b29fcde2.png">

.swiftlint.yml ファイルをルートディレクトリにおいて、ビルドを実行します。

<img width="1512" alt="005.png" src="https://qiita-image-store.s3.amazonaws.com/0/38924/642a3e2c-f98d-0a81-af8a-bca5288230d1.png">

末尾の行のチェックを無効にしたので、その警告は消えています。
また、1行の長さが200を超えるときは警告にしたので、エラーではなく警告になっています。

どのようなルールがあるかは、こちらの記事でまとめてくれています。

[SwiftLintのRules全まとめ](https://qiita.com/kagemiku/items/80e6d905dc0059c342b3)

## SwiftLintの効果

SwiftLintを導入することで、約80ものルールでコードをチェックできます。

たとえば、バグの原因となりやすいコードをチェックできます。

例：

- 強制アンラップは避けるべき
- オーバーライドされたメソッドは常にsuperを呼び出すべき

また、読みにくいコードにならないようにチェックしてくれます。
読みにくいコードは、構造化や抽象化がうまくできていない可能性もあるので、プログラムの構成を見直すきっかけにもなります。

例：

- ファイルの行数は多くなりすぎないように
- ネスト型は深くても1レベルの深さまで。その他の文は深くても5レベルの深さまでのネストに留めるべき

ほかにも、コードを書くときに間違う可能性を排除することもできます。

例：

- @IBOutlet変数はprivate修飾するべき

プログラムの書きかたは、人によって異なる場合があります。
SwiftLintを導入することで、そのような差異をなくして、コードの品質を安定させることができます。

また、SwiftLintはビルドをするときに自動的にチェックするので、チェックを忘れることもありません。

細かいルールチェックはSwiftLintにお任せして、ロジックを組み立てることに集中できますね。

こちらの記事 [Xcodeで使えるSwiftコードフォーマッター「Swimat」をつかってみた](https://qiita.com/toshi586014/items/1eab24c8e4b86fa10fb7) で紹介したSwimatをあわせてつかうと、さらに便利になりそうです。