CocoaPods Private SpecsでiOS用社内ライブラリを管理する

技術部のid:gfxです。iOSアプリ開発に欠かせないパッケージ管理ツールといえばCocoaPodsですが、これはPrivate Podsを作って社内ライブラリ専用のSpecs(private Specs)を管理することができます。

※ 2014/12/22追記 CocoaPods 0.35.0 でpod lintの--only-errorsが廃止されて--allow-warningsになったのでそのように変更しました

private SpecsがなくてもGit URLを指定することで社内用podspecを開発・管理することはできますが、private SpecsがあるとURL指定を簡略化したり依存関係の解決が簡単になるというメリットがあります。クックパッドでもすでに十数個のprivate podspecが登録されており、CookpadUIやAPI clientなどはpodspecとしてprivate Specsに登録されています。

そこで本エントリでは、Specsを運用している間に起きた問題などを共有します。なお、CocoaPodsのバージョンは v0.35.0 です。private Specsの仕様はまだ流動的なので、実際に運用に入る際は仕様をご確認ください。

概要

  • podspecでGit SSH URLを参照しているときは pod lib lint--allow-warnings をつける
  • pod repo push には --allow-warnings をつける
  • podspecでprivate Specsに依存する場合、pod lib lint には `--sources=...' でprivate SpecsのURLを与える

詳細

まずCocoaPods Specsの実体ですが、これはただの特別なパス構造を持ったgit repositoryです。仕様に従ったgit repositoryを用意すれば、検索こそできませんがPodfileでデフォルトのCocoaPods/Specs同様に使えます。

なお、本エントリでは以下の3つのgit repositoryを使います。

また、以下のようにprivate Specsを登録しておきます。PrivateSpecsExampleは空でかまいません。git repositoryの構成についてはpodコマンドがすべて面倒をみてくれるので、気にする必要がないのです。

$ pod repo add myspecs git@github.com:gfx/PrivateSpecsExample.git

podspecを作る

それでは、podspecをひとつ作り、private Specsに登録してみましょう。podspecのリポジトリはExamplePodです。

podspecは pod spec create ExamplePod で作り、生成されたExamplePodを修正します。 s.source はGitHub Enterpriseを想定しているのでGit SSH URLで指定しています。

-  s.source       = { :git => "http://EXAMPLE/ExamplePod.git", :tag => "0.0.1" }
+  s.source       = { :git => "git@github.com:gfx/ExamplePod.git", :tag => "0.0.1" }

ここでポイントその一ですが、pod lib lint は Git SSH URLだとfirewall越しにアクセスできない可能性があるというwarningsを出します 。そして、 pod lib lintはデフォルトでwarningsを致命的エラー扱いにします 。警告を致命的エラーにしないためには --allow-warnings オプションを指定しなければいけません。

このオプションのもとでの結果は以下のようになります。warningsは出ていますがvalidationはパスしています。

$  pod lib lint --allow-warnings

 -> ExamplePod (0.0.1)
    - WARN  | [source] Git SSH URLs will NOT work for people behind firewalls configured to only allow HTTP, therefore HTTPS is preferred.

ExamplePod passed validation.

なお、ここではやむを得ずwarningsを無視していますが、基本的にはwarningsを無視するべきではありません。他のwarningsが出ていないかの確認はするほうがいいでしょう。

podspecをpushする

次に、これをprivate Specsにpushしてみます。今回は同じrepositoryを使いますが、実際には社内のSpecsに対して行います。この時、前述の理由でGit SSH URLだとspec validationに失敗するのですが、このとき必要なオプションは--allow-warningsです。これが第二のポイントです。

# tagを打ってpodspecをpushする
$ git tag 0.0.1 && git push origin 0.0.1
$ pod repo push --allow-warnings  myspecs *.podspec

これでpodspecをprivate Specsに登録できました。あとはこのpodspecを使いたいプロジェクトのPodfileで以下のように source を指定すれば、そこに登録されたpodspecを使うことができます。

# Podfile
source 'git@github.com:gfx/PrivateSpecsExample.git'
source 'https://github.com/CocoaPods/Specs.git'

pod 'ExamplePod'

private podspecに依存したpodspecを作る

さて、このprivate podspecに更に依存したpodspecを作ってみます。内容はExamplePodと同じのExamplePod2をつくり、podspecの名前やパスを書き換えます。また、 s.dependency 'ExamplePod' を加えてprivate podspecへの依存を宣言します。

# ExamplePod2.podspec
  s.dependency "ExamplePod"

これを pod lib lint で検証すると、ExamplePodがみつからず以下の様なエラーになります。

$ pod lib lint

[!] Unable to find a specification for `ExamplePod` depended upon by `ExamplePod2`

ExamplePodはprivate podspecなので、そのままでは見つからないのです。しかしPodfileでは source でprivate Specsを指定できましたが、 pod lib lint によるpodspecの検証のときはPodfileは使われないので、private podspecを参照できないのです。そして pod repo add で指定したSpecsが暗黙のうちに使われるということもありません。

このため、pod lib lint--sources オプションでprivate Specsを指定する必要があります。デフォルトのCocoaPods/Specsも必要なので、カンマ区切りでURLのリストを渡します。これが三つ目のポイントです。

$ pod lib lint --allow-warnings --sources='git@github.com:gfx/PrivateSpecsExample.git,https://github.com/CocoaPods/Specs'

 -> ExamplePod2 (0.0.2)
    - WARN  | [source] Git SSH URLs will NOT work for people behind firewalls configured to only allow HTTP, therefore HTTPS is preferred.
    - WARN  | [iOS] Unable to find a license file

ExamplePod2 passed validation.

これで通常通り検証ができました。 pod repo push は検証のロジックがlintとは違うらしく、 --sources は不要です。

$ git tag 0.0.1 && git push origin 0.0.1
$ pod repo push --allow-warnings  myspecs *.podspec

これですべて問題なくできました。各種コマンドのオプションがが複雑なので、Makefileを作っておきましょう。CIでログなどをとるため、 --verbose もつけておくといいでしょう。

lint:
    pod lib lint --verbose --allow-warnings --sources='git@github.com:gfx/PrivateSpecsExample.git,https://github.com/CocoaPods/Specs'

release:
    pod repo push --verbose --allow-warnings  myspecs *.podspec

.PHONY: lint release

これでprivate Specsを使う準備はすべて整いました。

まとめ

CocoaPod private Specsはいくつか運用のポイントがあり、本エントリではそれらを紹介しました。private Specs周りはハマるところが多く、バージョンによって挙動が異なることも多いので、安定して運用できるよう情報を共有できればと思います。

デザイン設計に集中する時間を増やしてみよう

こんにちは! ユーザーファースト推進部のデザイングループのジョン・ジンホ(@img75)です。

前回、クックパッドのデザインプロセスについてご紹介しましたが、私からはクックパッドのデザインプロセスをより効率的にまわす為に、デザイナーとしてどのようなツールを活用しているのかを、今回は Adobe Photoshop CC のプラグインを中心にご紹介したいと思います。

デザイン設計に集中できる

Photoshop などでの作業効率を向上させると、デザイナーにとって貴重な「デザイン設計に集中できる時間」が生まれます。それは結果的にクックパッドを利用してくださるみなさまにより良い機能をすばやく提供できることにつながるので、私はいつもデザインするにあたって不必要な時間を減らす努力をしています。

最近、私は「撮るレシピ」(Android/ SPweb版)のデザインを担当しました。 「撮るレシピ」は、このレシピを覚えておきたいな、と思ったら写真に撮って簡単に保存、いろいろなところにあるレシピを一箇所に集めて見ることができるサービスです。ぜひ試してみてください。

このプロジェクトを進めるにあたって、さまざまな Photoshop プラグインを活用しました。ここではそのプラグインを以下の2つのカテゴリにわけてご紹介させていただきます。

  1. デザイン作業を手助けしてくれるプラグイン
  2. 書き出し、ガイド作成を手助けしてくれるプラグイン

デザイン作業を手助けしてくれるプラグイン

設計作業を支援するプラグインは様々ですが、今回のプロジェクトでは、私は以下のような2つのプラグインを多く使用しています。プロジェクトを進行しながら、どのように使用したのかを説明したいと思います。

ガイドを簡単に設定出来る​​

ウェブもそうですが、アプリを設計する際にも、それぞれの基本的な領域があります。インジケータ領域、アクションバー領域がそうです。私はこれらの基本的な領域に加え、各レイヤーを整列するためにキャンバスにガイドをたくさんかきます。しかしガイドをたくさんかくのは大変でそれなりに時間がかかってしまいます。その時間を減らすために Guideguide というプラグインを使っています。

f:id:img75:20141107161332p:plain

このプラグインでよく使うのは Grid notation機能です。これはガイドをセットとしてまとめることができる機能です。私の場合は上の画像のように 基本的なインジケーター領域とアクションバーの領域をあらかじめ設定したものをセットとして用意して、すぐデザインをはじめられるようにしています。クックパッドではたくさんのアプリを開発していますが、各アプリでトーンやマナーを合わせるする意味でもこのようなプラグインを使って領域を整えやすくすることはとても重要だと感じています。

アイコンをすばやく検索、適用する

アイコン素材を PSD や AI ファイルに保存して、そこから用途に合わせて使うようにしているデザイナーも多いと思いますが、私はこういう場面でもプラグインを活用して時間の短縮を実践しています。

f:id:img75:20141107161402p:plain

Flaticon はフリーライセンスのアイコン素材を検索することができるプラグインです。データが非常に膨大で、Photoshop のシェイプとして使用することができるようになっています。実際に使うアイコンとは少し違いますが、すばやくイメージを固めていくためのデザインプランを素早く練っていけるという利点があります。「撮るレシピ」のプロトタイピングでも実際に使用したアイコンは新たに作ったものですが、それまでのデザインに落し込んでいくプロセスでイメージを固めていくのに役立ちました。たとえば、「Search」というキーワードを入力すると、様々なスタイルのアイコンが表示されるので、そこからイメージに近いものを選んでさっと試してみることができます。

書き出し、ガイド作成を手助けしてくれるプラグイン

デザインを固めたらそれをエンジニアに伝えるためにデザインガイドの作成します。デザインガイドは、テキストやボタンの大きさ、色などを詳しく説明する必要があり、これを作成するのにもやはり時間がかかってしまいます。できるだけシンプルで素早く作成するためにも私はいくつものプラグインを活用しています。

使用したフォント、ボタンのサイズや余白を表示​​

私はガイドの作成にあたって、主にAssistor PSというプラグインを使ってデザインガイドを作成しています。上の画像のようにガイドボックス、フォントのサイズや色、種類、およびさまざまなガイドを作成することができます。

f:id:img75:20141107161443p:plain

(撮るレシピのガイド)

f:id:img75:20141107161457p:plain

(撮るレシピのActionBar)

デザインガイド文書は、UIデザイナーにとって非常に時間がかかる作業ですが、私の場合には、上記のデザインガイド文書を一日程度の時間で完了することができました。

コードで表現が可能な部分は、コードに渡す

f:id:img75:20141107161622p:plain

「撮るレシピ」の場合は、ボタンなどの部分について、画像ファイルを使用せずに、すべてコードで処理しました。コードで処理すると、アプリの全体的な容量を減らすことができるほか、読み込み速度も速くなります。これは画面ザイズが多様な Android 環境に対応するためにも重要です。これらのコードのエクスポート機能は、CSS3ps を利用して素早く簡単に作成しています。

9パッチを簡単に作る

Androidでは内容に応じて拡大縮小可能なグラフィックとして、「9 Patch」というグラフィックを扱うことができます。この9パッチグラフィックを理解していないと Android の UI デザインをすることが大変かと思います。どこまで拡張が必要な領域であることを考えて1ピクセル、1ピクセル描くのが時間がかかる作業なので、その作業を解決してくれる Clear Nine Patch というプラグインを利用してみるのはいかがでしょうか?

f:id:img75:20141107161643p:plain

「撮るレシピ」のActionBarでは、グラデーションを使用したグラフィックがあったので、9パッチで処理しました。ボタン1つで、9パッチ処理した画像を作成することができます。これで、Android の様々な画面に対応した、画像を作成することができました。

Tip! Photoshopの初期設定

プラグインと合わせて、私が用意しているPhotoshop の初期設定をご紹介します。初期設定を用意しておくことで、毎回画面サイズを確認しなくても、素早くデザインをはじめられます。

f:id:img75:20141107161700p:plain

まとめ

さて、いかがでしたでしょうか?クックパッドのデザイングループでは、クックパッドをご利用の方に、より迅速にサービスをご提供するため、ツールにも気を配っています。ツールを使うのは、デバイスが多様化していて書き出す画像のサイズを複数用意したり、9パッチのように特殊な画像を作成しなければならないなど、デザイナーがただただデザインをして終わりではなく、デザインをした後にエンジニアが迅速に作業を行えるようにデザイナーが考えなければないかと思います。

この他にも便利なプラグインなどは多くあるので、「こんなプラグインを利用してるよ!」というのがあればぜひ教えてください。今回のブログを通じて、より便利なツールがデザイナーに広まることができれば良いと思います。

aptly による apt リポジトリ管理

インフラストラクチャー部の宮下(@gosukenator)です。

クックパッドでは一部のサーバで Ubuntu を使い始めており、 apt リポジトリをどのように管理するのが良いのか、試行錯誤しています。aptリポジトリ管理で実現したいことは、主に次の2点です。

  • 自前でビルドしたパッケージの管理
  • リモートリポジトリから削除された旧バージョンパッケージの保全

このあたりをいい感じにできるツールはないかな、と社内で話していたところ、カルビ生焼け王 に教えてもらったのが aptly です。

aptly とは

公式サイトに「aptly is a swiss army knife for Debian repository management」とあるように、aptly は多機能な apt リポジトリ管理用ツールです。外部リポジトリのミラー作成、ローカルリポジトリの作成、リポジトリのスナップショット作成、スナップショット同士のマージ、S3 への publish 等の機能があります。「swiss army knife」と謳ってるのは伊達ではなく、かなり多機能でツールの全体像がつかみにくいのですが、オフィシャルサイトの Overview にわかりやすい図などがあるので、そちらを参照してください。

詳しい使い方等はここでは解説しませんが、興味のある方は Tutorial からトライしてみると良いでしょう。

aptly でやろうとしていること

現在 aptly を利用して、以下のことを実現しようとしています。

  • 自前パッケージ管理用のローカルリポジトリの運用
  • リモートリポジトリのミラー作成と、スナップショットを毎日保存してリモートから削除されたパッケージを保全
  • ローカルリポジトリとリモートミラーのスナップショットをマージして S3 へ publish

図にすると以下のようなイメージです。

f:id:MIZZY:20141106143626p:plain

ローカルリポジトリの運用

ローカルリポジトリは次のように作成します。

$ aptly repo create cookpad-repo

ローカルリポジトリにパッケージを追加するには次のように実行します。

$ aptly repo add cookpad-repo package.deb

リモートリミラー作成とスナップショット作成

リモートリポジトリのミラーは次のように作成します。

$ aptly mirror create -architectures=amd64 ubuntu-mirror \
  http://ap-northeast-1.ec2.archive.ubuntu.com/ubuntu/ trusty

リモートリポジトリとミラーを同期するには、次のように実行します。

$ aptly mirror update ubuntu-mirror

aptly mirror update を実行すると、リモートリポジトリと完全に同期されるため、リモートで削除されたパッケージはミラーでも削除されてしまいます。リモートで削除されても手元に残るよう、スナップショットを作成します。

$ aptly snapshot create ubuntu-mirror-20141106 from mirror ubuntu-mirror

毎日スナップショットを取得することで、リモートで削除されたパッケージの保全を行います。

ローカルリポジトリとリモートミラーのマージ

ローカルリポジトリとリモートミラーをマージするために、ローカルリポジトリのスナップショットを取得します。

$ aptly snapshot create cookpad-repo-20141106 from repo cookpad-repo

ローカルリポジトリのスナップショットと、リモートミラーのスナップショットをマージしたスナップショットを作成します。リモートで削除されたパッケージをすべて含めるため、リモートミラーから取得したスナップショットすべてをマージ対象に含めます。ローカルリポジトリのパッケージは基本的に削除しないので、最新のスナップショットのみマージ対象に含めます。

aptly snapshot merge --no-remove merged-snapshot-20141106 cookpad-repo-20141106 \
  ubuntu-mirror-20141106 ubuntu-mirror-20141105 ubuntu-mirror-20141104

マージしたスナップショットを S3 へ publish

マージしたスナップショットを publish します。初めて publish する場合はaptly publish snapshotを実行します。

$ aptly publish snapshot merged-snapshot-20141106 s3:cookpad:

2度目以降は aptly publish switch を実行します。

$ aptly publish switch trusty s3:cookpad: merged-snapshot-20141106

問題点

この方法の問題点は、publish switch 実行時に、差分ではなくすべてのパッケージをアップロードするため、非常に時間がかかる、ということです。特に、Ubuntu のパッケージアーカイブをすべてミラーしていると、4万個以上のパッケージをアップロードすることになり、EC2インスタンスからS3への転送に2時間以上時間がかかります。しかも、途中でネットワークエラー等で中断されると、最初からやり直しになってしまいます。

したがって、この方法は現実的でないため、他の方法を模索中です。私が aptly の機能をすべて把握できておらず、他にいい方法があるのに見つけられていない可能性もあるため、現在ドキュメントを読み漁っています。aptly がまだバージョン 0.8 なので、今後のバージョンアップでいい感じに解決できるようになるかもしれません。

もし他にいい方法やいいツールなどありましたら、ぜひ教えてください。