Swift Scripting

Swift はスクリプトを書くときに使えるでしょうか? Ayaka Nonaka さんが Swift でスクリプトを書き、Venmo の開発環境であった問題を解決したときのことについての発表です。


問題 (0:00)

これから Swift でスクリプトを書いたときのことについてお話しいたします。私たちは Venmo で、毎日デザイナーの人と一緒に働いています。やりとりがあることとして、画像などのアセットの受け渡しがあります。デザイナーがアイコンをアップデートし、私たちに渡してくれます。デザインをし、それらをエクスポートし、Dropbox か Slack で送ってくれるのが一番一般的でしょうか。時々、メールで送られてくるときもあります。これは方法が何であれ、ただ受け渡しができれば良いです。そして、エンジニアは受け取ったら、メインのレポジトリである venmo-ios に加えます。

実際、あまりこの過程は良くないことでした。簡単なやりとりですが、とてもたくさんのリンクやメッセージが飛び交います。デザイナーがあるエンジニアに画像を送り、またそのエンジニアが、別の機能で同じ画像を使っている他のエンジニアにもそれを送ったりしていました。まさにカオスでした。また更新があったときに、起こりうる別の問題もあります。間違って切り取りされた画像がアップロードされた場合などです。デザインをデバイスで確認するまで、その間違いに気付きません。そしてまた Slack メッセージなどでデザイナーの人に正しい画像を頼まないといけません。これもまた良くないことでした。

それに、デザイナーが一つの場所で簡単に全てのアセットを見れなかったも問題でした。以前は、Xcode プロジェクトか GitHub レポジトリで、いくつもフォルダをクリックしていき、やっとリソースのディレクトリを見つけるという方法でした。

解決法 (1:54)

これを解決するアイデアがありました。CocoaPods があるじゃないですか! venmo-ios レポジトリがメインのメインのレポジトリでしたので、アセット専用に venmo-ios-images を作ることにしました。私たちの新しいプロセスはこんな感じです。デザイナーがデザインをし、それらをエクスポートします。そして、デザイナーがその新しいレポジトリにプルリクエストを投げます。たまに上手くいかずプルリクエストのスクリーンショットが送られてくることがありましたが、この点において Venmo のデザイナーはかなり素晴らしかったです。そして、それをエンジニアがレビューし、良ければマージします。そして、新しい画像のための UIImage のカテゴリを新しく作ります。これは、コードのあちこちで画像の名前をタイプせずに済むようにです。その後は pod update を行うだけです。

スクリプトを書こう! (3:03)

この解決策はかなり簡単でした。しかし、エンジニアが新しい画像の UIImage のカテゴリを作る段階はどうでしょうか? ここで言う作るとはどういうことを指すでしょう? 新しいアセットが追加される度に手動で、それぞれのカテゴリの名前を追加していかないといけませんか? 正直、そのようなことはしたくありません。もう少し考えてみましょう。私たちエンジニアは怠惰なのです。じゃあ、何をすればいいのでしょうか? 全てを自動化しましょう!

今回の場合は、全てを自動で行うスクリプトを書くことにしました。スクリプトで、コマンドラインからそれらを実行できるようにしたいと思います。また、手軽にやりたいとも思っていました。スクリプトで考えられる選択肢としては、Xcode プロジェクトは選びたくないです。単にスクリプトを書くだけなのに、Xcode は立ち上げたくないです。私は、いつもスクリプトと聞くと、この xkcdコミックを思い出します。このコミックは、住所か何かをパースする問題を抱えている人たちがいて、そこにいきなりターザンが “正規表現、知っているよ!” と言いながら現れ、コンピュータをタップし、またターザンで消えていきといったようなものです。これこそがスクリプトです。決して Xcode プロジェクトではありません。そして、最後に、スクリプトを書くときは何か便利なライブラリが欲しいです。引数をパースしてくれるものや、インデントを整えてくれるものなどが欲しいところです。一から全て書きたくありません。

Swift を使う (4:07)

それで、スクリプトを書いて解決することは決まりました。そして、以前から普通に Ruby を使っていましたしやっぱり Ruby でやるのか Venmo の iOS Slack チャネルで聞いてみました。Swift はどうだろうかと思いましたが、直ぐにはできるかどうか分かりませんでした。Swift でスクリプトを書くこと自体、馴染みがなかったことだったため、Ruby では、2日で終わることももしかしたら、Swift では7日かかってしまうかもしれませんでした。しかし、Swift でできるのであれば、きっと楽しいだろうなと思いました。そして、ニューヨークオフィスから “Swift でやってみなよ!” という返事がありました。それを聞いたとき、挑戦が受け入れられた と思いました。そして、書きました! 書いたスクリプトは、画像のディレクトリをチェックし、全てのファイル名を確認していきます。そして、ファイル名を元に自動で UIImage のカテゴリを追加してくれるようなものです。この時点でファイル名は、デザイナーの人が注意して決めた名前になっています。

なぜ Swift を選んだでしょうか? Swift だと、普段から馴染みのある API が使えます。いつも Cocoa を使っていますし、普段のアプリ開発で行っていることが、スクリプトを書くときにも使えます。そして、別の理由として、言語自体が軽量で、Ruby や Python と似ています。その上、Swift のコンパイラはとても厳密です。言語は軽量だけど、コンパイラはとても厳密で、同時に二つの利点を得ることができます。

他にもメリットがあります。言語の依存が減らせます。先ほど、Ruby スクリプトを使っていると言いましたが、他にも Bash のスクリプトもあります。少し触るのが怖いので、私はあまり好んでは触りませんが、Swift で行えるのであれば、言語の依存が減らせることになります。プロダクションでない Swift を何か書きたいのであれば、スクリプトを書くのはうってつけだと思います。会社やチームで Swift に時間をあまり費やせないのであれば、実験として Swift でスクリプトを書いてみるのもいいかもしれません。そして、最終的には完全な Swift でアプリが書けるようになるのではないでしょうか。

Demo: スクリプト (7:12)

さて、それではスクリプトをどのように始めれば良いでしょうか? たとえば、hello-world.swift というスクリプトがあるとすれば、これを実行する一番簡単な方法は、xcrun swift hello-world.swift とコマンドを叩くことです。もう少し洒落たやり方にしたい場合は、chmod +x hello-world.swift でパーミッションを変更し、#!/usr/bin/env xcrun swift をどのようにこのファイルを実行するのかシバン(Shebang)を一番上に追加します。これで、./hello-world.swift と実行できるようになります。

どのようにするのか説明しましたが、ここでお見せいたします。hello-world.swift は、./hello-world.swift と実行すると “Hello, world!” とだけ表示されます。これはちょっとシンプルすぎるので、もう少し複雑なものを見ていきましょう。他に hello.swift というスクリプトがあります。これは、一つ引数をとり、“hello” とその引数で渡したものを出力します。./hello.swift swift とタイプした場合、“Hello, swift!” と表示されます。

これでもまだ簡単すぎると思いますので、もっと複雑なものを見てみましょう。私は少し前にサンフランシスコで Swift で Naive Bayes Classifier(単純ベイズ分類器) について発表を行いました。人間なら当然知っているようなサンプルデータをたくさん与えることで機械学習のモデルを訓練させます。たとえば、これにスパムとハムのサンプルを与えることでスパム判定器が作れます。新しい例を与えたときにハムかスパムか判定してくれるようなものです。そしてここに、これをスクリプトで行ったものがあります。たくさんのサンプルで訓練を行い、精度を上げています。

スクリプトで単純ベイズ分類器を作り、4つのディレクトリを参照しています。これらのディレクトリには、スパムの訓練データとハムの訓練データ、ハムのテストデータとスパムのテストデータがあります。このスクリプトを走らせたとき、ハムとスパムのたくさんの訓練データをそれぞれ読み込み、テストデータを使って、ハムとスパムの判定を行います。これを実行したところ、ハムのテストは、全て正しく判別されましたが、スパムの場合は、100個中42個だけがスパムと判定されました。正直、スパム判定は良くできていません。このデモで使われたスクリプトはここにあります。

依存関係の管理 (11:28)

それでは、依存関係はどうでしょうか? なぜ単純ベイズ分類器のコードを全てスクリプトの中に書いているのでしょうか? また、ハムとスパムのサンプルデータを他の場所を参照するようなオプションを渡したい場合はどうでしょうか? 他のフレームワークなどはどのようにすれば追加できるでしょう?

依存関係について考えたとき、いくつか思い当たる選択肢があります。一つは、Git submodules です。とてもシンプルですが、少し非力です。今回のところ、少しシンプル過ぎました。もう少し色々とやってくれるものが良かったです。そして、CocoaPods を考えました。CocoaPods は iOS 開発で使っています。バージョン 0.36 のリリースで Swift にも対応されましたし、かなり素晴らしいです。依存関係の管理も行ってくれます。しかし、これは Xcode プロジェクトに依存してしまいます。スクリプトを Vim で書いていますので、Xcode には依存したくありません。なので、CocoaPods は良いとは言えませんでした。

Carthage (12:49)

そんな時に、Carthage というものを聞きました。Swift に対応していて、それに依存関係の管理も行ってくれます。手動でプロジェクトにフレームワークを追加しなければいけませんが、私にとっては好都合でした。そして、少し Carthage について調査したところ、Cartfile を作成し使いたいものをそこに定義するだけで良いことが分かりました。PrettyColorsAlamofire を追加し、carthage bootstrap を実行しました。そうすると、Build というディレクトリが作成され、そ���にビルドされたフレームワークが出力されます。Carthage/Build/iOSAlamofire.frameworkPrettyColors.framework ができていました。

それで、これらのフレームワークをどうすれば良いでしょうか? インターネットで少し調べると、ライブラリのフレームワークに追加するだけで良かったみたいです。しかし、全てのプロジェクトでこれらの追加したフレームワークを参照させたくない場合はどうでしょうか? Swift のドキュメントをまた調べたところ、フレームワークを特定するパスが指定できるようでした。Carthage は特定のディレクトリにフレームワークを出力するので、シバンでフレームワークの参照先のパスを指定するようにするだけで問題ありませんでした。

また、別にポエムを出力するだけの簡単なスクリプトを書きました。ポエムを色付きの文字にしたかったので、PrettyColors をインポートしました。そして、ターミナルの1行目から赤、青、黒、青と出力されるようにしました。これでテキストの出力にカラフルにすることができました。これは簡単にフレームワークを読み込むシンプルな例です。

足りないもの (15:18)

では、足りないものは何でしょうか? ですが Carthage のサポートをしているフレームワークは全て使うことができるので、既にだいぶたくさんのことが行えることかと思います。私は Venmo のアセットの自動生成のスクリプトは、Vim で書きました。その時は、Swift 用のシンタックスハイライトとオートコンプリートがありませんでした。どうやって書けたのかわかりませんが、やりました。Vim とかでもオートコンプリートができるようになればかなり素晴らしいことでしょう。それともう少し、依存関係の管理が良くなれば良いと思いました。これら今お見せしたことが、インターネットで調べたり、他の人と協力して、分かったことです。ちょっとしたハックですが、Carthage を使ったりしてとてもクリエイティブな方法だと思います。

便利なツール! (16:20)

もっと便利なツールが使えるところがあります。多くの便利なツールがリリースされていますが、しかし Swift でスクリプトを書く時にそれらが使えると知らない人が多いかと思います。他にも誰もが一緒に使えるものが必要だと思います。しかし、たくさんのものが既にリリースされています。後に、Keith Smiley が書いた Vim Syntax highlighter があると知り、今は使っています。とても便利です。

他にも PrettyColors や JSON パースの Argo、オプションのパースの CLIKit、セマンティックバージョニングの SemverKitBananaKit などもあります。そして、皆さんも是非、便利なものを書いて共有してください。そうすれば、もっとみんなで Swift でスクリプトが書け、良いコードが書けるのではないでしょうか。

追記 (17:08)

これで私の発表は終わりですが、今朝少し今の発表の内容がくつがえることが起こりました。昨晩のディナーの後、CocoaPods のメンバーである KyleBoris と話していました。その時、もちろん CocoaPods は好きだけれども発表で Carthage について話すと伝えました。すると Kyle が Boris がちょうど CocoaPods のプラグインで同じようなことが行える、Rome というプラグインを書いたということを教えてくれました。もし、この歴史について知っている人なら、この名前がウケることかと思います。

その場では、ただのジョークだと思い、真剣に捉えなかったですが、今朝 6:30 に目覚め、Twitter を見ていました。すると Boris からこのメンションがあり、そこには Rome のレポジトリのリンクが貼られていました。リンク先を確認したところ、どうやらフレームワークを出力する CocoaPods プラグインのようです。なので、Swift でスクリプトを書く時 Carthage と CocoaPods の両方とも使えることになります。かなり使い方は簡単で、なんでもその Rome のディレクトリに出力できるみたいです。

Q&A (18:52)

Q: Foundation や 他の Cococa フレームワークを使うとき何か困るようなことはありますか?

Ayaka: 全て使えます。自然言語処理のフレームワークである NSLinguisticTagger は、本来 iOS のみのフレームワークなのですが、使えています。

Q: Swift でスクリプトを書く時に Swift のバージョン指定を行うことに関して何か考えはありますか? たとえば、Swift 1.2 ではなく Swift 1.1 を指定する方法はありますか?

Ayaka: 良い質問ですね。でも、私はその答えはわかりません。誰か会場で知っている人はいませんか。

観客: 多分、Swift の中でシェルコマンドを使って、xcode-select で変更するのがいいです。



野中 彩花

野中 彩花

VenmoのiOSリードで最近はSwiftばかり書いています。iOS 4の頃からiOS開発を始め、テイラー・スウィフトの曲を聴きながらSwiftを書くのが大好きです。これまでSwiftにおける自然言語処理や、スクリプティング、VenmoアプリをSwiftで書き直したことなどをテーマに講演しました。東京生まれなので、東京のカンファレンスで話せることがとても楽しみです!よろしくお願いします。