ライブラリ・SDKにSwiftLintを導入するベストプラクティス

ライブラリ作るの、楽しいですよね。
作ったからには、沢山の人に使ってもらいたいですよね。
そして、ソースコードをきれいに保つためにSwiftLintも使いたいですよね。
でも、環境によってSwiftLintがErrorを吐いて使われなくなる、とかは避けたいですよね。
というわけで、そのあたりをまとめてみました。

## なぜまとめたか
今関わっているプロジェクトで以下の現象に遭遇したために、掘り下げてみました。

- mixpanel-swiftを導入
- SwiftLintを`0.13.2` > `0.15.0` にアップデート
- `carthage update --platform iOS --no-use-binaries` が通らなくなる
- ふむ🤔

## SwiftLintバージョン違い問題
上記の現象について詳しく調べてみたところ、

- mixpanel-swiftが想定するSwiftLintのバージョン:
    - 0.13.2
- ローカルのSwiftLintのバージョン
    - 0.15.0

と想定するSwiftのバージョンに差異があったために引き起こされていたことがわかりました。
つまり、いつものこいつによって、引き起こされていたのです。
<img width="692" alt="Screen Shot 2017-02-04 at 00.23.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/33395/aa370343-c48a-a36d-a8dd-ae74f4c2fe2e.png">

## どう解決できるか？
自分でも[PlayListPlayer](https://github.com/nerd0geek1/PlayListPlayer)などのライブラリを開発しているため、この件について色々と考えてみました。  
例えば、

- SwiftLintのScriptを削除する
    - SwiftLintが手軽に実行できなくなるため、ダメ🙅
- swiftlintに対してエイリアスを設定(上記の例であれば、`mixpanel-swiftlint`等)
    - 悪くはなさそう。
- ライブラリのリポジトリにswiftlintのバイナリを追加
    - swiftlintのバージョンが固定されるのは良いが、そもそもライブラリのリポジトリに追加すべき？

などなど。

## 岸川さんからのアドバイス
ただ、何かしっくりこないなーと思いつつ、つぶやいたところ、
<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">1. SwiftLintをリポジトリに追加して参照する<br>2. SwiftLintにエイリアス切って参照する<br>やりたいことは、<br>- 開発中はSwiftLintでチェックしたい<br>- CI上ではチェックなくても良い<br>だから、2. かなー。1. だと想定外のケース出ないかな―？</p>&mdash; Kohei Tabata (@nerd0geek1) <a href="https://twitter.com/nerd0geek1/status/826597175903686656">February 1, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

岸川さんからアドバイスが！

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr"><a href="https://twitter.com/k_katsumi">@k_katsumi</a> そうですよね。<br>ただ、今回は<br>mixpanel-swiftの使っていたバージョン：0.13.2くらい？<br>自分のローカル環境：0.16.1<br>となっていて、ルールの変更があった故にcarthageでのインストールがコケる、といった感じでした。</p>&mdash; Kohei Tabata (@nerd0geek1) <a href="https://twitter.com/nerd0geek1/status/826653125918412800">February 1, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

SwiftLintを別ターゲットに分ける？なるほど、わからん🤔
<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr"><a href="https://twitter.com/nerd0geek1">@nerd0geek1</a> おお、そういうこともあるんですね。Carthageでエラーになるのはターゲットを分けるのがいいと思いますね。SwiftLintは別ターゲットにしてしまって、外部からBuildだけ、Testだけ、Build+Lintなどを制御するようにするのがいいと思います</p>&mdash; kishikawa katsumi (@k_katsumi) <a href="https://twitter.com/k_katsumi/status/826669228933517313">February 1, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

なるほど、SwiftLintのScript Phaseのみを含むターゲットを作ればよくて、
Realmプロジェクトにその実装があると。

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr"><a href="https://twitter.com/nerd0geek1">@nerd0geek1</a> Realmのプロジェクト見てもらうのがいいと思いますけど、SwiftLintのターゲットはScript Phaseだけしか持ってないので、そういうことは起こらないです。メインのビルドなどと全く関係なく独立してるのでその方がスマートだと思います。</p>&mdash; kishikawa katsumi (@k_katsumi) <a href="https://twitter.com/k_katsumi/status/826675506586148865">February 1, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

## Realmのプロジェクト構成を確認
普段あまり使わないような形でSwiftLintがターゲットとして独立して追加されていました。
<img width="882" alt="Screen Shot 2017-02-06 at 06.26.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/33395/f9eeab80-483c-04be-260e-aa65b5a10d5c.png">
調べてみたところ、`ターゲットの追加 > Cross-platform`で選択できるAggregateというものを使っているようです。
![Aggregate.png](https://qiita-image-store.s3.amazonaws.com/0/33395/52f59278-0e15-f4f8-5a75-458456829954.png)

## Aggregateとは
`Xcode Target Aggregate`で検索してみましたが、公式ドキュメントは見つけられませんでした。。。
他で探したところ、[こちら](https://pewpewthespells.com/blog/managing_xcode.html#target-agg)が参考になりました。
要約すると以下のようになります。

- 直接、何かをビルドするものではない
- 他のターゲットのビルドステップを整理する手段として役に立つ
- `run scripts`や`copy files`などのような追加的な`Build phases`に対処する時に特に役に立つ。

SwiftLintでは`run scripts`を追加することになりますので、ぴったりです。

## 実際に導入
PlayListPlayerに実際に導入してみました。
導入手順としては
1. `ターゲットの追加 > Cross-platform > Aggregate` を選択
2. `Run Script`を追加し、SwiftLint実行用のコードを追加

するだけです。
これによって、SwiftLintのBuild Schemeも追加され、独立してSwiftLintを実行できるようになります。

<img width="1440" alt="Screen Shot 2017-02-06 at 08.30.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/33395/d9a787f9-bdb9-b3a6-1936-14960ffd477d.png">


## テスト時にのみ実行
以上の実装では、Lint用Targetを選択しないとLintが実行されないため、
メインターゲットの選択中、かつテスト時にのみ(🙅ビルド・アーカイブ時)実行されるように設定します。
以下のように設定するだけです。
![SwiftLintTarget.png](https://qiita-image-store.s3.amazonaws.com/0/33395/7deefb02-1f43-9ea2-bba3-c61dec8939c7.png)


## まとめ
アプリのプロジェクトと違い、ライブラリでは効果的なLintの実行方法が異なることがわかって頂けたかと思います。
是非、ご自身のライブラリにも適用してみてください！

## 参考
- [Managing Xcode](https://pewpewthespells.com/blog/managing_xcode.html#target-agg)
- [Creating a Static Library in iOS Tutorial - raywenderlich](https://www.raywenderlich.com/41377/creating-a-static-library-in-ios-tutorial)