Konifar's WIP

親方!空からどらえもんが!

iOSDCで『Sketchからアイコン切り出し ⇒ Asset Catalogを自動生成 ⇒ 差分があればPullRequest』という流れをCIで自動化する話をしました

2ヶ月くらい前にiOSDC 2017のプロポーザルを出したんですが、2つ出したうちの1つが採択されたので登壇してきました。

speakerdeck.com

サンプルリポジトリこちらです。資料だけだとわかりにくい部分もあると思うので、少し補足しておこうと思います。


Import Sketch Icons to Asset Catalog on CI

f:id:konifar:20170917160518p:plain

  • Sketchからアイコンを切り出して、Asset Catalogを生成して、差分があったらiOSリポジトリにPull Requestを出すというのをCIで自動化している話です。
  • 私の所属するQuipper Limited.で実際に運用している知見です。

@konifar

f:id:konifar:20170917163425p:plain

  • konifarという名前で、TwitterGitHubをやっています。
  • プライベートで、専業主婦の嫁さんとエンジニアリングやエンジニアの仕事に関する雑談をするyome.fmというPodcastをやっています。
  • 普段はQuipper Limited.で、スタディサプリというアプリを作っています。Androidエンジニアです。

iPhone7

f:id:konifar:20170917163720p:plain

  • 普段使っている端末もAndroidなんですが、嫁さんに「村八分にあうぞ」と脅されまして、今日はiPhone7を持ってきました。iPhone最高ですね。

My first Swift!

f:id:konifar:20170917163837p:plain

  • そんな自分も今回の発表のサンプルリポジトリを作ったことで、初めてSwiftのリポジトリを持つことができました。
  • (ここで拍手が起こり、なんて温かいコミュニティなんだ…!と感動しました)

How do you manage the icons?

f:id:konifar:20170917164100p:plain

  • 皆さんどうやってアイコンや画像を管理してますか?たぶん大体の方はこんな感じのフローなのではないかと思います。
  • まず、デザイナーさんがアイコンを作ってくれて、それをzipファイルやSketchファイル、あるいはSketchからExportしたZeplinなんかでエンジニアに渡されますよね。
  • で、アイコンを受け取ったエンジニアはそれをXCode上でドラッグ&ドロップしてAsset Catalogに入れます。もしかしたら、簡単なスクリプトで組み込むようにしてるかもしれません。
  • そのあと確認できたらPull Requestを出して、マージされたら晴れてiOSプロジェクトに入ります。
  • だいたいこんな感じですよね。

Problems

f:id:konifar:20170917171542p:plain

  • 別にこのやり方でもいいんですけど、いくつか問題もあります。
  • まず、デザイナーさんとアイコンのやり取りするの地味にめんどくさいですよね。「アイコンください」と言ってSlackでもらっても、ちょっと後でやろうとしたらSlackでは流れてしまうし、何か間違っていた時もやりとりしなきゃいけない。
  • Asset Catalogにドラッグ&ドロップするのもめんどくさいです。特に新しい機能を作ってたくさんアイコンが必要な時とか超めんどくさい。
  • デザイナーさんから見ても、AndroidiOSの両方のアイコンを管理したり、各エンジニアとやりとりするのは骨が折れるんじゃないかと思います。

Make it automatic on CI!

f:id:konifar:20170917172812p:plain

  • 弊社Quipperでは、この流れをCIで自動化しています。
  • ここからが本題です。

Overview

f:id:konifar:20170917173148p:plain

  • ざっくり全体像を説明します。
  • まず、DesignリポジトリにデザイナーさんがSketchファイルをpushします。このSketchファイルには、アプリ内で使うアイコンや画像が全て入っています。このファイルについては後で詳しく説明します。
  • masterにマージされるとCIが走り、Sketch toolというのを使ってSketchファイルから画像を切り出します。その切り出した画像からAsset Catalogを生成して、差分があればPullRequestを出します。ここまでがCIで自動化されている部分です。
  • あとは、そのPull Requestをエンジニアが確認してマージするだけです。
  • EngineerとDesignerとのやりとりが疎になっています。距離が遠くなるというわけではなく、人間同士がやり取りしなくていいところに、間にCIに入ってもらったというわけです。

How it works?

f:id:konifar:20170917210052p:plain

  • では、どうやって動いているのかをサンプルリポジトリを例にして少し詳しく見ていきます。

1. Push Sketch file to GitHub

f:id:konifar:20170917211704p:plain

  • まず、デザイナーさんがDesign RepositoryにSketchファイルをpushする部分です。

images.sketch

f:id:konifar:20170917211948p:plain

  • このSketchファイルは、実際にQuipperで運用されているものです。アプリ内で使われる画像とアイコンが全て入っています。
  • それぞれのアイコンはExportの設定をしてあります。

images.sketch

f:id:konifar:20170917212424p:plain

  • 2つだけルールを決めています。1つはネーミングルールです。
  • 自動でアイコンを切り出してiOS/Androidのプロジェクトに組み込むので、このSketchファイルの段階でネーミングルールを決めておかないと後でリネームが必要になってしまいます。
  • 大きく画像とアイコンの2種類があって、画像の場合は img_ から始まる名前に統一しています。アイコンは ic_ から始まるようにして、コード上でわかりやすいようにsuffixにサイズも入れるようにしています。
  • また、基本的にアイコンは白色で統一しています。色をつける時はコードでTintをかけています。これは、アプリサイズを減らすためです。

git-sketch-plugin

f:id:konifar:20170917213456p:plain

  • ここで、もしかしたら「デザイナーがGitを使えるのか?」と思う方もいるかもしれません。実際に運用してみた経験から言うと、たぶん大丈夫です。
  • git-sketch-pluginというSketchプラグインがありまして、これを使うとSketchからGUIでcommitやpushといったGitの操作を行えます。
  • さらにこのプラグインのよいところは、commit時にプレビューのpng画像を生成してくれるところです。Sketchファイルはバイナリファイルなので、GitHubで管理するとPullRequest時に何のアイコンが追加/編集されたのか全くわかりません。このプラグインはArtboardというSketchの中のグループのような単位でプレビュー画像を生成するので、その画像の差分を見て何が変わったのか確認できるようになります。

2. Export icons by Sketch tool

f:id:konifar:20170917214321p:plain

  • このSketchファイルがmasterにマージされるとCIが走り、sketchtoolでアイコンがExportされます。

Sketch tool

f:id:konifar:20170917214850p:plain

  • sketchtoolは、Sketchの各種操作をコマンドラインで実行できる最高のツールです。
  • Sketch本体のツールに組み込まれていて、サンプルではinstall_sketch.shで都度インストールして使っています。
  • sketchtool export slices images.sketch を実行すると output オプションで指定したディレクトリにアイコン画像が切り出されます。

3. Create Asset Catalog

f:id:konifar:20170917215125p:plain

  • 次に、切り出されたアイコンからAsset Catalogを生成する部分です。

Asset Catalog Structure is simple

f:id:konifar:20170917215317p:plain

  • Asset CatalogはXcode上でアイコンをドラッグ&ドロップして作られますが、実際にはとてもシンプルな構成です。
  • Assets.xcassets の中に アイコン名.imageset というディレクトリが作られ、そこに解像度ごとの画像が入っています。
  • Contents.json というファイルもシンプルで、中には各拡張子ごとの画像の情報が記述されているだけです。

import_to_ios.sh

f:id:konifar:20170917215838p:plain

  • import_to_ios.shの中の export_images() という関数で、Asset Catalogを生成しています。
  • まず切り出されたアイコンを一つずつ見ていき、 アイコン名.imageset ディレクトリを作るためにアイコン名を抽出します。
  • アイコン名のsuffixが _android だったら処理をスキップしている部分がポイントです。アイコンの中には、Androidのみで使われるものもあります。例えば、Floating Action ButtonのアイコンはAndroidだけで用いられることがあります。これらを別ファイルで管理するのは面倒なので、「suffixが _android だったらAndroidのみのアイコン」というルールで運用しています。
  • そのチェックをくぐりぬけたら、 imageset ディレクトリを作って中にアイコンをコピーし、Contents.jsonを生成します。

Contents.json

f:id:konifar:20170917220711p:plain

  • Contents.json を作っている create_contents_json() の関数は、先程の Contents.json の中身を愚直に書き込んでいるだけです。とてもシンプルですね。

Vector support

f:id:konifar:20170917221011p:plain

  • 最近では、Vector画像をそのままAsset Catalogで使えるようになりました。
  • PDF形式の画像を使うのですが、sketchtoolはPDF形式のExportにも対応しているので同じような仕組みで自動化することができます。
  • formats オプションに pdf を指定し、Contents.json の生成部分も各解像度ごとではなくPDFファイル1種類のものに変えればOKです。
  • 実はまだサンプルリポジトリでは対応できていないので、今後やっていきます。もし興味があればPull Requestを送ってもらえるとありがたいです!

4. Send PR to iOS repository

f:id:konifar:20170917221951p:plain

  • Asset Catalogができたら、最後はPull Requestを作る部分です。

Send Pull Request

f:id:konifar:20170917222233p:plain

  • send_pull_request() という関数の中で、 git checkoutgit add します。何も難しいことはしていません。
  • ポイントは1つだけで、差分があったときのみPull Requestを送りたいので git status --porcelain | grep $ASSETS_DIR で変更があったかどうかをチェックしています。

5. Check and merge PR

f:id:konifar:20170917222525p:plain

  • 変更があったらPull Requestが作られるので、あとはエンジニアがそれを確認してマージするだけです。

Pull Request

f:id:konifar:20170917222632p:plain

  • これは実際にサンプルリポジトリで作られたPull Requestです。
  • Contents.json とアイコンが追加されていることがわかります。
  • iOSリポジトリでCIを回してDeployGateで配信するようにしていれば、デザイナーさんだけでアイコンの変更結果を確認することも可能です。便利ですよね。

Feedbacks

f:id:konifar:20170917222950p:plain

  • 全体の流れの説明は以上です。
  • 最後に、よかったところと改善したほうがいいところをお伝えします。

Good points

f:id:konifar:20170917223114p:plain

  • よかったところは、AndroidiOSの画像/アイコンを一元管理できるようになったところですね。
  • また、アイコン受け渡し時のデザイナーさんとのやり取りが減って、エンジニアは自動的に作られるPull Requestを見るだけでよいというのもいい感じです。
  • Asset Catalogを手動でドラッグ&ドロップして作ることもないので、ヒューマンエラーも防げます(と思っていたんですが、実は登壇後に「それZeplin使っても防げるよ」とTwitter@hirothingsさんに教えていただけました!ありがとうございます!Zeplin最高ですね)

Improvements

f:id:konifar:20170917223630p:plain

  • 逆に改善点としては、アイコンの名前のつけ方のルールをもう少し厳密に決めておくべきだったというのがあります。例えば、検索のアイコンを追加した時に、 ic_search とつける人もいれば ic_magnifying_glass とつける人もいるんですよね。まぁどちらでもいいのですが、エンジニアからするとルールは統一しておきたいところです。アイコンの名前は形を表すものにするか、それとも役割や機能を表すものにするかを明確にしておいた方がよりコミュニケーションを減らせると思います。
  • あと、たまにExportの設定を忘れたままSketchファイルをpushしてしまうことがあって、その時は当然ですが差分が出ないのでPull Requestが生成されません。「PushしたのにまだPull Requestが出ません」という無駄なやりとりが発生してしまうので、差分がなかった時はSlackなどに通知するようにした方がいいかもしれません。
  • 最後に、これはできたらいいなという話なんですが、この一連のスクリプトをgemなどのライブラリにした方がCoolでしたね。他のiOSプロジェクトがどんな構成で作られているかわからなかったので今回のサンプルリポジトリでは実現できなかったのですが、今後やっていくかもしれません。sketchtoolのようなSketchプラグインでもよさそうですね。

Thanks!

f:id:konifar:20170917224724p:plain

  • 発表は以上です。ありがとうございました。

Questions

f:id:konifar:20170917224817p:plain

  • ここから先は当日は話しませんでしたが、質問として考えられるものを2つ書いてあります。

Which CI service is the best?

f:id:konifar:20170917224944p:plain

  • CIサービスを選定する上では、1つだけ制約があります。
  • sketchtool は macOS に依存しているので、CIもmacOSに対応しているものでないと動きません。
  • 無料で対応してくいるのは、TravisCIBitriseです。サンプルリポジトリでは、TravisCIを使っています。
  • ただ、TravisCIは無料だとpublic queueが詰まって実行までに1時間くらい待たされることもあります。
  • CircleCIは有料プランであればmacOS環境を使えます。

How to introduce this automation?

f:id:konifar:20170917225616p:plain

  • この自動化を既存のプロジェクトに導入する場合、どう進めていったらいいのかという話です。
  • まず、Sketchファイルを管理するデザイン用のリポジトリを作る必要があります。
  • もしAndroidiOSリポジトリでアイコン名が統一されていなければ、リネームして揃えましょう。
  • 名前が揃ったら、アイコンを1つだけ images.sketch に追加します。
  • サンプルリポジトリを参考にして、スクリプトを作り、ローカルで実行してみましょう。
  • ローカルで実行できるようになったら、CIで動くようにします。
  • CIで動いたら、少しずつアイコンを images.sketch に追加していきます。あとは最後までやりきるだけです。

iOSDCはアウェーで緊張していたのですが、聞きに来ていただいた皆さんは非常に温かくて安心しました。iOSDC全体の感想はまた別途書こうと思いますが、まずは運営の皆さん、お疲れ様でした!聞きに来ていただいた皆さん、ありがとうございました!