blog.yujinakayama.me

CocoaPodsのプラグインを作る

Dec 14th, 2013

この記事はiOS Second Stage Advent Calendar 2013の14日目の記事です。

概要

Cocoaのライブラリ管理ツールであるCocoaPodsですが、2013年11月14日にリリースされたバージョン0.28で、プラグイン機能が導入されました。

本記事ではこのプラグインの作り方を紹介します。

CocoaPodsプラグインとは

CocoaPodsのプラグイン機能を利用すると、podコマンドに任意のサブコマンドを追加することができます。標準サブコマンドのpod installpod updateと同列な、新たなサブコマンドの作成が可能です。

CocoaPodsはRubyで作られたツールなので、プラグインもRubyで書く必要があります。また、プラグインの配布にはRubyGemsを利用します。

このプラグインの仕組みそのものは、CocoaPodsプロジェクトが依存gemとして利用しているCLAideによって可能になりました

プラグインを作る

ここではcheck-latestというサブコマンドを定義する、cocoapods-check_latestというgemを作ることにします。これは指定したライブラリのpodの最新バージョンが、そのライブラリのGitHubリポジトリの最新のバージョンタグと同一かどうかをチェックするコマンドです。

試しにCocoaPodsで導入してみたライブラリがうまく動作しないと思ったら、実は最新のpodspecで指定されているライブラリのバージョンが、そのライブラリの本当の最新バージョンではなかった(この表現わかりにくいですね)という場面がたまにあったりします。なので事前にpodspecが最新かどうかチェックしたいのですが、そのライブラリのGitHubのリポジトリをブラウザで開いてタグ一覧を確認、というのも割と面倒なルーチンワークなので、この作業を自動化することにします。

まずは新しくgemのプロジェクトを作成します。Bundlerを利用するのが良いでしょう。なお、gem名はcocoapods-プラグイン名にすることが公式に推奨されています。そうしないとプラグインとして動作しない訳ではありませんが、RubyGemsでの検索のしやすさも考慮した慣例になっています。RubyGemsをcocoapods-検索すれば、現在公開されているプラグインの一覧が見られる訳ですね。

$ bundle gem cocoapods-check_latest
...
$ cd cocoapods-check_latest

コマンドクラスを作成する

Pod::Commandクラスを継承したコマンドクラスを作成します。

# lib/pod/command/check_latest.rb

module Pod
  class Command
    # クラス名からサブコマンド名が自動生成される。
    # CamelCaseの単語間には"-"が挿入されるため、この場合"check-latest"になる。
    class CheckLatest < Command
      # 必須。`pod help`時に一行で表示される概要を記述する。
      self.summary = 'Check if the latest version of a pod is up to date'

      # 任意。`pod help check-latest`時に表示される、より詳細な説明を記述する。
      # 未指定の場合、summaryの内容が表示される。
      self.description = 'Some long description...'

      # コマンドラインから引数を受け取るコマンドの場合は必須。
      # `pod help check-latest`時に、
      #
      # Usage:
      #     $ pod check-latest [NAME]
      #                        ^^^^^^ ここに表示される。
      self.arguments = '[NAME]'

      # 引数を受け取るコマンドの場合は必須。
      # argvから、後の処理で必要な引数をインスタンス変数に取り出しておく。
      # この処理はsuper呼び出し前に行うこと。
      # argvは、CLAide::ARGVのインスタンス。
      # https://github.com/CocoaPods/CLAide/blob/v0.4.0/lib/claide/argv.rb
      def initialize(argv)
        @name = argv.shift_argument
        super
      end

      # 引数を受け取るコマンドの場合は必須。
      # 引数が不正な場合はhelp!メソッドにメッセージを渡して中断する。
      def validate!
        super
        help!('A pod name is required.') unless @name
      end

      # 実際の処理を記述する。
      def run
        # 長いので省略。実際の内容は以下を参照して下さい。
        # https://github.com/yujinakayama/cocoapods-check_latest/blob/v0.0.1/lib/pod/command/check_latest.rb
      end
    end
  end
end

ちなみに、CocoaPodsのサブコマンドを定義するということは、CocoaPods本体のAPIを利用できる訳ですが、現時点ではどのクラスやメソッドがpublic APIであるかという宣言がされていません。将来的にはこの辺りも整備されてくるとは思いますが、現状では利用対象のクラスやメソッドが存在しているかどうかのチェックや、例外処理などをこまめに行うしかない模様です。どちらにしてもCocoaPodsは未だにバージョン1.0未満の初期開発段階なので、Semantic Versioning的にもAPIの互換性はあまり重視されないフェーズではあるのですが。

作成したコマンドが、podコマンド実行時に読み込まれるようにする

コマンドを作成しただけでは、CocoaPods本体がその存在を認識できません。

Rubyの$LOAD_PATHに登録されたパス直下(通常はプロジェクトのlibディレクトリ直下)に、cocoapods_plugin.rbというファイルを作成し、その中で上記のコマンドクラスが記述されたファイルをrequireします。これによってpodコマンド実行時にカスタムコマンドクラスがロードされるようになります。

# lib/cocoapods_plugin.rb

require 'pod/command/check_latest'

動作確認をする

実際にpodコマンド経由で動くか確認してみましょう。

$ echo "gem 'cocoapods', '~> 0.28'" >> Gemfile
$ bundle install
...
$ bundle exec pod help | grep check-latest
    * check-latest   Check if the latest version of a pod is up to date
$ bundle exec pod check-latest viewcontroller


-> AMBubbleTableViewController
   - Homepage: https://github.com/andreamazz/AMBubbleTableView
   - Latest pod version:0.5.1
   - Latest version in original repo:0.5.1


-> APPinViewController
   - Homepage: https://github.com/Alterplay/APPinViewController
   - Latest pod version:1.0.2
   - Latest version in original repo:1.0.2


-> ARGenericTableViewController
   - Homepage: https://github.com/arconsis/ARGenericTableViewController
   - Latest pod version:1.0.0
   - Latest version in original repo:1.0.1
   Outdated!

...

プラグインを配布する

あとはRubyGemsのルールに則ってgemを公開しましょう。

cocoapods-check_latest.gemspecdescription, summary, homepageあたりの項目を記述し、BundlerのRakeタスクで公開します。

$ rake release

おわりに

実際に作成したcocoapods-check_latestプラグインを公開しました。gem install cocoapods-check_latestでインストールができます。