Swift lint

Swift lintを使ってみる
======

これ使ってみてーってswift lintと、それを紹介している勉強会の動画を渡されたけど、最後まで聞くことに耐えられなれなかった、、、
何度か目の再生停止で、原文当たった方が幸せじゃない？と気付いたので、ついでに自分なりにまとめておく。

ちなこれ
[realm/SwiftLint: A tool to enforce Swift style and conventions.](https://github.com/realm/SwiftLint)

なにそれ
----
lintってのは聞いたことはあった。文法解析して、なんかエラー出すやつ。それのswift版。らしい。
うまく設定することで、指定したルールに沿わない書き方を見つけるとエラーやワーニングとして騒いでくれるとのこと。~~全く余計なお世話~~ ありがたや。
チーム作業でコーディング規約を強制させたい場合にきっと便利。

セットアップ
----
Homebrewは気持ち悪いので使いません。
~~どうせ自分では使わないだろうし~~プロジェクトのgitにそのまま突っ込む。

```bash
cd <project_dir>
git submodule add https://github.com/realm/SwiftLint.git
```

するとディレクトリが作れられてくれるので、インストールしとく。途中で管理者権限を要求されます

```bash
cd swiftLint; make install
```

続いて、Xcode Projectに呼び出しを仕込む。
TargetのBuild Phaseを開いて、左上の「＋」でNew Run Script Phaseを追加。エラー文をちょっと弄ってある。

```bash
if which swiftlint >/dev/null; then
  swiftlint
else
  echo "warning: SwiftLint not installed. To install, run 'cd <proj_dir>/swiftLint; make install'"
fi
```

lintなので、Compile Sourcesの次くらいに実行されるように
マウスドラッグで移動させておく。

cmd-bでビルドして、エラーとワーニングが山のように量産されて~~lint氏ね~~ わーすごいやーってなったら成功。

設定ファイルを作る
-----
設定ファイルはプロジェクトのディレクトリに`.swiftlint.yml`を置いとけばいいらしい。

とりあえずPodsとSwiftLintを排除。

```bash
cd <project_dir>
vi .swiftlint.yml
```

```yaml
# Swift Lint Rules

excluded: # excluded directory
  - Pods
  - SwiftLint

```


ちなみにコード中に特定記法のコメントを書くことでも操作できるらしいけど、どうせやらない。し、本来的にやるべきでもない。

```swift
// swiftlint:disable <rule>
// swiftlint:enable <rule>
let noWarning2 = NSNumber() as! Int // swiftlint:disable:this force_cast
```



設定を書いてく
-----

ワーニング うぜええええええ！！！！
…となるので、順番に殺していく。

![trailing_whitespace.png](https://qiita-image-store.s3.amazonaws.com/0/66847/b7e70240-969c-9b9c-166c-d9597ba9e984.png)

最後の`(trailing_whitespace)`に注目。これがルール名らしい。

ルールについてはソース読んでね！という公式の投げっぷり
> See the [Source/SwiftLintFramework/Rules](https://github.com/realm/SwiftLint/blob/master/Source/SwiftLintFramework/Rules) directory to see the currently implemented rules.

なので、もうなんとでもなーれ、って気持ちで眺めましょう。


```swift
// SwiftLint/Source/SwiftLintFramework/Rules/TrailingWhitespaceRule.swift
public func validateFile(file: File) -> [StyleViolation] {
        return file.lines.filter {
            $0.content.hasTrailingWhitespace()
        }.map {
            StyleViolation(ruleDescription: self.dynamicType.description,
                severity: configuration.severity,
                location: Location(file: file.path, line: $0.index))
        }
    }
```

なんだ、このクソ実装．．．
空行の場合は例外視とか普通つけるでしょ、、、なんで設定の余地ないの？

こういうケースの時とか…
![emptyline_tailingspace.png](https://qiita-image-store.s3.amazonaws.com/0/66847/3b6a4960-666f-b828-7e14-c0f939486d58.png)

というような場合は disabled_rules: に書いてあげるとルールが無効になり、平和が訪れます。

```swift
disabled_rules:
  trailing_whitespace #since has no option to treat empty line,,,
```

追記した後にもう一回cmd-bしてみると、ちゃんとワーニングが消えます。
よしよし。


あとは無意味に厳しい行数制限をテキトーに緩めたりとか

```yaml
line_length: #max length of each line
  warning: 200
  error: 500

type_body_length: #max line num of class body
  warning: 1000
  error: 3000
```

と、いうように、まずはルールを殺戮し尽くすところから始めるべきらしい。です。

とりあえず、現状、こんな感じ。完全に個人的な主観です。

```yaml
disabled_rules:
# vvv will never enable
  - trailing_whitespace
  - opening_brace
  - closing_brace
  - statement_position
  - todo
  - force_cast
  - force_try
  - file_length
  - cyclomatic_complexity
# vvv may enable in someday
  - return_arrow_whitespace
  - control_statement
# vvv may enable in nearday
  - legacy_constructor
  - legacy_constant
  - trailing_newline
#
```

カスタムルール
-----
ここまでで標準のルールの残念さ加減に気付いた皆さんに嬉しいお知らせで、なにやらカスタムルールも定義できるらしいです。
正規表現を書いてあげると良いらしい。

例えば、こんな感じ。
classやfuncの宣言の間には1行空行を入れましょう、というやつ。

```yaml
custom_rules:

  spacer_line:
    name: "Spacer Line"
    regex: "(\}\n)([ \t]*)((public )|(private )|(internal )|(override ))*((class)|(func))"
    message: "Need empty line between func or class"
    severity: error
```

あと`match_kinds`形態分析結果ので絞り込めるっぽいのだけど、どれがどんな要素として扱われるのかがナゾで使い物にならないのが残念。
（現時点では）気合の正規表現で無理やり誤魔化すしかなさそう。


感想
----
最初のエラーの数には呆れたけど、いらないのをガンガンとブラックリストに突っ込んで機能を絞り込めばそこそこ使えそう。
warning/errorがXcode上で扱えるので、ビルド遅くなる以外は良さそう。

コロンがどこにあろうと（読み易ければ）なんでもいいや、な自分としては、規約違反はwarning出すようにしといて、commit前にちょろっと整えるようにするくらいで使うと結構便利かもしれない。
なお、自分は{をifの次行に書くことは許さない教なので、そういったところは戦争・・・もとい、事前の話し合いが重要かと思います。

その他
-----
すっげーたくさんエラー出てるなあ。でもコードに見覚えないなー、、、
と思ったらSwiftLintが自分自身のコードに対して評価してワーニングとエラー吐きまくってたの、すっごくロックだわね…