Cocoaのライブラリ管理ツール「Carthage」のセットアップ&操作方法

## 「Carthage」とは？

Cocoa(iOSやmacOSなど)のライブラリ管理ツールです。
「カーセッジ」または「カルタゴ」と読みます。
どちらの読みが正しいのかはわかりません。。

## CocoaPodsとの主な違い

Cocoaのライブラリ管理ツールといえば「CocoaPods」という有名なツールがあります。
主な違いは以下の2点です。

- 事前にライブラリをビルドする  
そのため、1回あたりのビルド時間が延びない
- `.xcworkspace` ファイルが生成されない  
`.xcodeproj` ファイルのままプロジェクトを扱える

1回あたりのビルド時間が延びないのは大きなメリットであり、それが理由でCocoaPodsから乗り換える方も多いと思います。

## Cocoaのライブラリ管理ツール一覧

Cocoaのライブラリ管理ツールを表にまとめると以下の通りです。

|名前|ライブラリのインストール時間|1回のビルド時間|
|:--|:--|:--|
|CocoaPods|速い|遅い|
|Carthage|遅い|速い|
|SwiftPM|速い|遅い|

「SwiftPM」とは「Swift Package Manager」の略であり、Apple公式のライブラリ管理ツールです。
https://github.com/apple/swift-package-manager

## 環境

- OS：macOS Mojave 10.14
- Xcode：10.0 (10A255)
- Swift：4.2
- Carthage：0.31.2

## Carthageのセットアップ

### Carthageのインストール

Homebrewからインストールします。

```shell-session
$ brew install carthage
```

Carthage自体のセットアップはこれだけです。
設定なども不要です。

## ライブラリのセットアップ

ライブラリのセットアップは手順が多いので、漏れがないように注意する必要があります。

### カレントフォルダの変更

まずはプロジェクトのルートフォルダに移動します。

```shell-session
$ cd {.xcodeprojファイルがあるフォルダ}
```

### Cartfileの編集

プロジェクトのルートフォルダに `Cartfile` という拡張子なしのファイルを作成し、使いたいライブラリを記述します。

Cartfileのフォーマットは以下の通りです。

```ruby:Cartfile
github "{作者名/リポジトリ名}" {記号} {バージョン}
…
```

記号の意味は以下の通りです。

|記号|意味|
|:--|:--|
|>= 1.0|1.0以降の最新バージョン|
|~> 1.0|1.0と互換性がある最新バージョン|
|== 1.0|1.0と一致するバージョン|
|なし|最新バージョン|

GitHub以外にあるライブラリも使えますが、あまり使わないと思うのでここでは割愛します。

例：
Realm(NoSQLのDB)をインストールします。
2018/12/16現在の最新バージョンが `3.13.0` なので、それと互換性のあるバージョンを指定します。

```ruby:Cartfile
github "realm/realm-cocoa" ~> 3.13.0
```

### ライブラリのビルド

Xcodeを終了し、以下のコマンドを実行します。

```shell-session
$ carthage update --platform iOS
```

`Cartfile` に記述した全てのライブラリにおいて、ソースコードがダウンロードされ、ビルドが始まります。
ライブラリによっては __ダウンロードやビルドに時間がかかります__ 。

`--platform` でプラットフォームを指定しないと、macOSやwatchOS用のビルドも行ってさらに時間がかかるため、指定すべきです。

ビルドが完了すると `Cartfile.resolved` というファイルが生成されます。
こちらのファイルには実際にダウンロードしたライブラリのバージョンが記述されています。

例：

```ruby:Cartfile.resolved
github "realm/realm-cocoa" "v3.13.0"
```

Realmの `v3.13.0` がダウンロードされたことがわかります。

ちなみに、ライブラリ名を指定することで、対象のライブラリのみビルドすることもできます。

```shell-session
$ carthage update {ライブラリ名} --platform iOS
```

例：

```shell-session
$ carthage update realm-cocoa --platform iOS
```

### ライブラリのビルド(既存のプロジェクト)

既存のプロジェクトの場合、開発者同士でライブラリのバージョンを合わせる必要があると思います。
その場合、`carthage update` の代わりに `carthage bootstrap` を実行すれば `Cartfile.resolved` に記述されているバージョンをインストールします。

```bash
$ carthage bootstrap --platform iOS
```

そのため、 `Cartfile` や `Cartfile.resolved` はバージョン管理に含めるべきです。

### ビルドの確認

ライブラリのビルドに成功すると、 `Carthage/Build/iOS/` フォルダに `{ライブラリ名}.framework` ファイルが生成されます。
生成されていない場合、ビルドに失敗している可能性があるため、 `carthage update` 時の出力を確認してください。

例：
Realmの場合、 `RealmSwift.framework` と `Realm.framework` の2つがビルドされます。

<img width="609" alt="スクリーンショット_2018-12-16_22_30_05.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/138245/a78e57e8-90bf-4174-770f-3247db868d06.jpeg">

### ライブラリを製品ターゲットに追加

以下の手順でビルドしたライブラリをプロジェクトに追加します。

__Xcodeでプロジェクトを開く
TARGETSで製品ターゲットを選択 > General - Linked Frameworks and Librariesに `.framework` をドラッグ&ドロップする__

例：
Realmの場合、2つともプロジェクトに追加します。

<img width="881" alt="スクリーンショット_2018-12-16_22_32_02.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/138245/e1b14b02-9859-f1e9-51b8-fb912417765c.jpeg">

__Build Phases > +をクリック > New Run Script Phase__
以下のスクリプトを記述します。

```bash
/usr/local/bin/carthage copy-frameworks
```

__Input Files > +をクリック__
ライブラリのパスを相対パスで記述します。

```
$(SRCROOT)/Carthage/Build/iOS/{ライブラリ名}.framework
```

<img width="1025" alt="スクリーンショット_2018-12-16_22_36_43.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/138245/810f6c9e-29b6-7446-02e6-9d4e0a9ca430.jpeg">

### ライブラリをテストターゲットに追加

ライブラリを単体テストで使う場合、テストターゲットにも追加する必要があります。

__TARGETSでテストターゲットを選択 > Build Settings - Framework Search Paths__
以下を記述します。

```
$(PROJECT_DIR)/Carthage/Build/iOS
```

<img width="1023" alt="スクリーンショット_2018-12-16_22_44_00.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/138245/7d524f01-da9c-6e72-ab45-c39f19c8f00c.jpeg">

こちらを記述しないと単体テストの実行時に以下のビルドエラーが発生します。

```
No such module '{ライブラリ名}'
```

### 単体テスト時の注意

Target Membershipにテストターゲットを含めるとテストできないことがあるため、製品ターゲットのファイルのチェックはOFFにします。

<img width="277" alt="スクリーンショット_2018-12-16_22_48_24.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/138245/69e72cbe-e370-1e46-58c1-bc3e6db4b840.jpeg">

単体テストを書くときは `@testable import {製品ターゲット名}` でインポートすることでテストできます。

追加したライブラリも `import {ライブラリ名}` でインポートする必要があります。

例：
<img width="292" alt="スクリーンショット 2018-12-16 22.59.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/138245/4ccd5e2c-dad3-734d-834d-8369ab70bad4.png">

importせずに呼び出そうとすると、以下のビルドエラーが発生します。

```
Use of undeclared type '{ライブラリ名}'
```

### バージョン管理から無視する

ビルドしたライブラリはサイズが大きいため、 `Carthage` フォルダごとバージョン管理の対象外にすべきです。

Gitを使っている場合、以下を `.gitignore` に追加するのみでOKです。

```:.gitignore
Carthage
```

## ライブラリの削除

こちらの記事でわかりやすく説明されていたので、本記事では割愛します。
https://qiita.com/y-some/items/85c2606928b0f1ef5d72

## おわりに

これでライブラリが使えるようになります！
OSSのライブラリはうまく活用することで開発のコストや品質を大幅に上げることができるため、Carthageを使って素晴らしいライブラリライフを送りましょう:relaxed:

## 参考リンク

- [Carthage/Carthage: A simple, decentralized dependency manager for Cocoa](https://github.com/Carthage/Carthage)
- [Carthage/Artifacts.md at master · Carthage/Carthage](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md)
- [Realm: Create reactive mobile apps in a fraction of the time](https://realm.io/docs/swift/latest/)