CocoaPods Private Specリポジトリの作成と利用方法


CocoaPodsでは公式なSpecリポジトリがGitHub上に公開されています。
https://github.com/CocoaPods/Specs

従来からこれとは別にPrivateなSpecリポジトリを用意する方法がありました。さらにCocoaPods 0.34から ``Podfile`` に宣言できるようになったことで明示的に参照するSpecリポジトリが指定できます。


```ruby:Podfile
source 'https://github.com/makotokw/CocoaPodsSpecs.git'
source 'https://github.com/CocoaPods/Specs.git'
```

今回は自前のSpecリポジトリの作成と利用法について書きます。

## 自前のSpecリポジトリが必要なケース

CocoaPodsでは``Podfile``に以下のように書くことで本家のリポジトリにない自前のPodを使うことができます。

```ruby:Podfile
pod 'WZYFlatUIColor', :git => 'https://github.com/makotokw/CocoaWZYFlatUIColor.git'
```

これで済むならSpecリポジトリを用意する必要はありません。Specリポジトリが必要になる理由としては以下のようなものがあります。

* Podの依存関係やバージョンを適切に管理したい
* 柔軟にPodのバージョンを指定したい


### Podの依存関係

長くライブラリを開発しているとさらに共通の処理を別の共有ライブラリに作成して逃したくなります。そうするとPodとPod間に依存関係が必要になります。CocoaPodsではPodごとのPodspecファイルに ``dependency`` シンタックスを使って依存関係を宣言することができます。

```ruby:CommonLibrary.podspec
spec.dependency 'CommonLibrary2'
```

しかし``dependency`` シンタックスでは ``:git`` オプションがサポートされておらずSpecリポジトリにあるPodでなければ依存関係に指定できません。Specリポジトリを用意しないで解決するには``dependency`` シンタックスを使わずにアプリケーション側で ``Podfile`` に以下のようにPodに依存するPodを全て指定する必要があります。

```ruby:Podfile
pod 'CommonLibrary', :git => 'https://github.com/makotokw/CommonLibrary.git'
pod 'CommonLibrary2', :git => 'https://github.com/makotokw/CommonLibrary2.git'
```

しかし、この回避策ではアプリケーションの開発者がライブラリの依存関係を全て把握し、依存関係に変更があった場合に``Podfile``の内容を同期しなければなりません。

### Podのバージョン管理

長く開発していると全てのアプリケーションが同じバージョンのライブラリを利用することは難しくなります。例えば、ある共有ライブラリでv2.0からiOS5を切るといった選択をした場合、iOS5対応のアプリではv1.*を使うということで以下のように書きたいものです。

```ruby:Podfile
pod 'CommonLibrary', '~> 1.0'
```

これを ``git``オプションで参照するPodの場合は ``branch`` や ``tag`` を使って管理することになります。

```ruby:Podfile
pod 'CommonLibrary', :git => '...', :branch => 'develop'
pod 'CommonLibrary2', :git => '...', :tag => '1.0'
```

この場合 ``'~> 1.0'`` のようなバージョン比較の記述が利用できません。

またアプリケーションとライブラリの依存ならともかく、以下のようにライブラリ間でバージョンの依存関係ができてしまうとアプリケーション開発者がすべてのライブラリの依存関係を ``Podfile`` で管理するのは難しくなってきます。

 * CommonLibrary ``v1.0`` が CommonLibrary2 ``v1.*`` に依存する
 * CommonLibrary ``v1.1`` が CommonLibrary2 ``v2.*`` に依存する

CommonLibraryがどのライブラリのバージョンに依存しているかはカプセル化されているべき内容です。自前のSpecリポジトリを用意すれば ``:git`` オプションに頼ることなくライブラリを利用でき、バージョン利用の柔軟性や、Pod間の依存関係をライブラリの開発者に任せることができます。

それまではアプリケーションごとの ``Podfile`` で頑張ってはいましたが管理が限界に来ていたため ``sources`` の指定が義務付けられたCocoaPods 0.34のタイミングで自前のSpecリポジトリの利用に踏み切りました。

## Specリポジトリを作成する

本家がそうであるようにSpecリポジトリはただのGitリポジトリとして存在します。Private Specリポジトリと呼ばれることもありますが、実際にPrivateかどうかはこのGitリポジトリの公開範囲できまります。GitリポジトリがPrivateであればそのSpecリポジトリはPrivateなものになりますし、GitHubなどでPublicなSpecリポジトリとして公開することも可能です。

Specリポジトリは以下のような構造でファイルを管理することで利用できます。

```
.
└── [SPEC_NAME]
    └── [VERSION]
        └── [SPEC_NAME].podspec
```

しかしPodのリリースの度にバージョンフォルダを作成してPodspecファイルを追加するのは面倒なので、これを行ってくれる ``pod repo push`` コマンドを利用します。

### リポジトリの登録

``pod repo push`` を使う準備作業として、まず push する先のSpecリポジトリを名前付きで登録します。Gitのリモートリポジトリのようなものだと考えると良いでしょう。

```
pod repo add REPO_NAME SOURCE_URL
```

たとえば以下のように書くことができます。

```
pod repo add makotokw git@github.com:makotokw/CocoaPodsSpecs.git
```

### Podを登録する

SpecリポジトリにPodを登録するにはPodspecファイルを用意して、事前に登録しておいた``REPO_NAME``に対して ``pod repo push`` を使うことで行えます。このコマンドでは指定したPodspecファイルの内容に準じて適切な階層にファイルを追加し、Gitのpushまでを行ってくれます。

この作業はPodの新しいバージョンをリリースする度に行い、既に登録済みのバージョンがあった場合は既存のものを差し替えることができます。

```
pod repo push makotokw CommonLibrary.podspec --allow-warnings
```

``pod repo push``ではPodspecファイルの内容を検証し、問題があれば登録できません。本家のSpecリポジトリに登録する際に使われることもあってか検証は厳し目になっています。

例えばLicense情報の指定がないと警告になり登録できないなど、PrivateなSpecリポジトリの場合には本質的ではない検証もあるため ``--allow-warnings`` で警告を許可するのも有効です。

なお ``pod spec lint NAME.podspec`` で登録する前に検証ができます。この際に``dependency``で本家のSpecリポジトリに登録されていないPodに依存している場合は ``--sources`` にSpecリポジトリを指定する必要があり、以下のような形で実行します。警告を無視するのであれば``--allow-warnings``を指定できます。

```
pod spec lint NAME.podspec --sources=makotokw --allow-warnings
```

``pod spec lint``ではPodspecファイルで``source``に指定したソースコードが検証されます。以下のように指定している場合はGit上のソースコードが検証の対象になります。

```CommonLibrary.podspec
spec.source       = { :git => "https://github.com/makotokw/CommonLibrary.git", :tag => "v1.0.0" }
```

ローカルのソースコードで確認したい場合は ``pod lib lint``コマンドを代わりに使うことができます。

```
pod lib lint NAME.podspec --sources=makotokw,master --allow-warnings
```

## 作成したSpecリポジトリを利用する

冒頭にも書きましたが作成したSpecリポジトリを利用するには``Podfile``の[source](http://guides.cocoapods.org/syntax/podfile.html#source)で宣言します。

```Podfile
source 'https://github.com/makotokw/CocoaPodsSpecs.git'
source 'https://github.com/CocoaPods/Specs.git'
```

Specリポジトリは宣言した順番に検索されます。(以前は名前順でSpecsリポジトリが検索され、本家が``master``という名前のため自前のリポジトリ名が``M``より前か後ろかで挙動が変わる問題がありました)

従って自前のSpecリポジトリを先に宣言しておけば、本家のSpecリポジトリで公開されているPodをforkし、自前のSpecリポジトリに登録することでforkした方を優先して利用するといった使い方ができます。(ただし``pod install``時に同名のPodが複数のSpecリポジトリから検出されると警告はでます)

## Specリポジトリを使う際の注意点

CocoaPodsのバージョンによって許可されていたシンタックスが非推奨になったり、挙動が変わることによってSpecリポジトリにあるPodspecファイルを書き直すなどメンテナンスが必要になることがあります。

実際問題、0.35の際に循環依存のチェックが入って修正を余儀なくされました。本家のSpecリポジトリでもみんなで修正している様が伺えます。[#12783](https://github.com/CocoaPods/Specs/pull/12783), [#12819](https://github.com/CocoaPods/Specs/issues/12819)

これはSpecリポジトリに限った話ではありませんが、CocoaPodsを使う場合は[更新情報](https://github.com/CocoaPods/CocoaPods/blob/master/CHANGELOG.md)をチェックして特にBreakingな修正には動作を確認しておく必要があります。時にはXcodeのバージョンにも左右されることもあります。

CocoaPodsは便利ですが深く付き合うにはそれなりの覚悟も必要です。

## まとめ

Private Specリポジトリ自体は昔からありましたが、``source`` の宣言がサポートされようやくライブラリ開発者にとってもアプリケーション開発者にとっても利用しやすくなったと思います。幾つものアプリケーションを開発している方はうまくライブラリを再利用することで生産性を高めていくとよいでしょう。

一方で本家のリポジトリに登録する、自前のSpecリポジトリを公開するなど自作のPodをオープンにしてくことも忘れないようにしたいと思います。

CocoaPodsはバージョン更新によって挙動が変わるのでチームで使うにはどのバージョンを使うか、いつアップデートするかなどSpecリポジトリの管理も含めてチームの中で誰が責任をもって運用するかが重要です。