Toybox
このたび、Toyboxというコマンドラインユーティリティを作ったので紹介します。
普段、Swiftのコードを書いていて、
- 思いついた実装を試してみたい
- コードレビューで別の書き方を提案したときに、ちゃんと動くか動作確認したい
- バグっぽい挙動をしてるので最小コードで確かめたい
- もっといい書き方ができないか試行錯誤したい
なんてときによくXcodeのPlaygroundを使うと思います。
でも、Playgroundの作成は操作が煩雑だし、名前や保存場所を決めなきゃダメなのがものすごく億劫で、デスクトップにMyPlayground.playground
みたいなファイルがたまり続けていて困っていました。
怠け者の僕は、新しく作るのすら怠いので、デスクトップに適当に落ちてる過去のPlaygroundを開いてスニペットを書いていたりして散々な運用だった。
というわけで、CLIからPlaygroundを簡単に生成、管理してくれるツールを作りました。
$ toybox create Foobar
とかやると、勝手にFoobar.playground
が開かれて即座にスニペットが書ける。
一度作成したものは
$ toybox list $ toybox open Foobar
などで管理できる。特定の場所に全て管理されてファイルが散らばらない。
思想はghqを参考にしている。
開発中から仕事に使ってみていたけれど、僕のような片付けのできない人間にとっては、ファイルがあちこちに散らばらなくて割と重宝している。
さらに機能とか
詳しくはREADMEを読めば書いているんだけど、便利機能もいくつか用意している。
名前すら決めるのが億劫な人向けに自動的に名前を生成してくれる機能もある。
$ toybox create # 20160929015019.playground
ただ、これを使うとあとからどのファイルかよくわからなくなるのであまりオススメできない
$ pbpaste | toybox create --input
このように、標準入力から中身を作る機能もついていて、コピペしたり、curlでgistを読み込んできたいときなどに役に立つかもしれない。
どうぞご利用ください。Homebrewかインストーラーから導入できます。
作ってみて
Swift 3
iOSのツールなんだからSwiftで書かれているべきだろ!と特に考えもせず、今回は初めて全てSwift3で書いてみた。 Swift3は最高の言語で、移行するのは面倒だけど、新規に書く分には快適に書けて非常に良かった。
Swift3を使うための問題は、やはりライブラリの互換性。 しかし、メジャーなものはあらかたSwift 3化が進んでいるし、自分が使いたいやつが2のままでも3化に貢献するチャンスだと思うので、ガンガンPRを送っていくと良いと思う。
今回はオプションパーサーライブラリとして、Carthageに使用されているCommandantというライブラリを使ってる。 Toyboxを開発し始めた当初は3対応がされていなくて、forkして使っていたり、本家にも3化のPRを送って進めていたのだけど、 ほかにも親切な方が進めてくれて、結局僕のコードはマージされなかった。
とはいえ、Swift3化の際にCarthageの中の強い人にレビューしていただいて、なるほどこう書くのか〜とSwift3移行への知見が深められて良かった。 優しい世界。
Swiftでコマンドラインツールを作る
Commandantが使えてしまえば、あとは作るだけ。開発はほとんど、Carthageの構成を参考にして実装した。
Playgroundのパースやファイル操作といった部分はToyboxKit
など、新しいFrameworkを作ってそこに押し込み、
本体のバイナリはオプションをパースして、適切にFrameworkを叩くだけといった構成が良さそうだった。
全体の工程の中で、一番大変だったのが、実行可能形式にして使ってもらうようにするというところ。
Swiftには統一されたバイナリの配布方法ないのでCarthageのMakefile
やHomebrew Formulaをパクってきていい感じに書き換えた。
良い方法はないので、結局Makefileをガリガリ書いて、ビルドしたバイナリとFrameworkを適切な位置に配置してあげるというインストーラーを書くしかなさそう。
感想
僕には便利だし、今後使っていきそうなプロダクトができてよかった。ほかにも刺さる人がいれば嬉しいです。
前述したようなオプションパーサーのSwift3化や配布周りでひたすらMakefileを書くみたいなYak Shavingが多すぎてSwiftでコマンドラインツールを作るのは面倒だった。 こんな感じのツールはRubyなりで書いて、Rubygemsにドーンするのに限る。
Swiftはその辺のエコシステムがまだまだ弱くて、パッケージとして配信する方法もHomebrewしかない上に、手順も正規化されていないので、かなり敷居が高い印象だった。
Swift Package Managerは現在、アプリから使うFrameworkの管理という問題に絞って開発が進められている。
将来的にはgo get
のように実行可能バイナリを簡単に扱う機能も拡張していってほしい。
今後の展望
- Alfredから使いたいからWorkflow作る
- 作ってるときに思いついたけど、最後に作ったやつを開く便利コマンドがほしい
- 今回、適当なPlaygroundのパーサーを書いたからこの辺を機能増やしてライブラリ化すると良いかもしれない