皆様こんにちは、Yahoo! JAPANアプリの開発をしている林(@kazuhiro4949)です。
ソフトウェアを開発する際には形式的なボイラープレートコードを書くことがよくあリます。iOSアプリ開発でもよくある話で、その支援のためのライブラリもさまざまなものが作られています。例えばSwiftGen(外部サイト)やmockolo(外部サイト)などが有名です。
そういったツールを有効に活用しているプロジェクトも多いのではないかと思いますが、自動化したいものが既製品として提供されていないケースもあるでしょう。
そんな時に、自分の作業を効率化させるための自動生成ツールを開発できたら便利だと思いませんか? この記事では、私がYahoo! JAPANアプリ開発時の業務効率化のために行っているコード自動生成ツール開発手法を、実装パターンとしてご紹介します。
各開発フェーズにおける自動生成手法
多くの方がコード自動生成としてイメージされるのは、ターミナル上でコマンドを使って生成する場面ではないでしょうか。私はあまりコマンドラインツールを使うことはなく、Xcode上でのコード実装の流れの中に組み込むことが多いです。
iOSアプリ開発ではほとんどの方がXcodeを使って実装しているでしょう。Xcodeの機能をうまく使うと、実装しながらそのままの流れでコード生成を行うことができて便利です。
具体的にどんなタイミングで何ができるかを下の表にまとめました。細かく言うと他にもいろいろやりようはありますが、私が実装を自動化したいなと思ったらおおむねこのパターンを使います。
方法 | 実行されるタイミング | |
---|---|---|
1 | Xcode Template | ファイル作成時 |
2 | Xcode Source Editor Extension | ファイル編集中 |
3 | Code Snippet | ファイル編集中 |
4 | Pre-actions | ビルド開始前 |
5 | Run Script | ビルド中 |
6 | Post-actions | ビルド終了後 |
番号は作業をするときのフェーズを指しており、どのタイミングで自動生成したいかによって使い分けています。それでは、各フェーズの方法について見ていきましょう。
Xcode Template
XcodeでiOSアプリ開発をする時には必ずテンプレートを利用しているかと思います。Xcodeのメニューから「File > New > File…」を選択した時に出てくるあのウィザードです。
それを使って、空のSwiftファイル作成を行ったり、UIViewController・NSObjectあるいはXCTestCaseの子クラス作るというのをしているかと思います。
初めから用意されているテンプレート以外にも実は自分自身で用意できます。また、デフォルトのテンプレートと同じくテキストフィールドやチェックボックスから生成されるコードを変更できます。
例えば以前社内で関わっていたプロジェクトでは、Template Methodパターンを使った特定のクラス構成をルールづけていました。レイヤードアーキテクチャを採用したプロジェジェクトに関わっている方は、そういうケースをすぐに想像できるかと思います。
そんな時に一式のクラス群が自動生成ができ、とても便利な機能です。
ではどのように作成するのかというと、残念ながら公式ドキュメントは私の知る限りでは存在しません。ただし、既に用意されているテンプレートが下記ディレクトリに存在しており、それらをコピーアンドペーストしながら加工して作っていくことができます。
/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File Templates/
例えば現時点(2021/12/22)で最新のXcode 13.2.1において、SwiftUIのViewのテンプレートは以下の場所に配置されています。
> ls /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File\ Templates/MultiPlatform/User\ Interface/SwiftUI\ View.xctemplate/
TemplateInfo.plist ___FILEBASENAME___.swift
見ると2つのファイルが用意されており、TemplateInfo.plist
にはウィザード上に表示されるアイコンや入力項目などを定義します。
作ったテンプレートは以下に配置して、Xcodeを再起動すると使えるようになります。
~/Library/Developer/Xcode/Templates/
Xcode Source Editor Extension
コード編集をしながらやる方法としては、Xcode Source Editor Extensionが使えます。Xcodeは制限付きの拡張機能を持っており、現在編集中のファイルを入力としてその内容を書き換えられます。拡張機能はmacOSアプリのExtensionとして開発し、バンドルしたmacOSアプリをインストールすると利用できます。制限としてはファイル単位での書き換えしかできず、ファイルを跨いで型推論しているコードで凝ったことはできません。
Yahoo! JAPANアプリでは開発時に必ずテストコードを書いているのですが、定型作業になってしまっているインターフェース作成やモック作成をこの方法で自動化しています。拡張機能はApp Storeで配布することができ、実際にプロジェクトで使用している自動生成ツールも公開しています。
- DI Helper for Swift(外部サイト)
- Access Level Changer for Xcode(外部サイト)
また、実装ノウハウについては以前開催されたiOSDC Japan 2021(外部サイト)で共有していました。もしご興味のある方はそちらもご覧になってください。
Code Snippet
少し自動生成の定義を広げ過ぎかもしれませんが、編集中に使える手法としてCode Snippetも紹介します。
Xcode Templateのようにあらかじめコードを用意して選択することが可能です。決まったコードしか生成できないため自動生成と呼べるかは微妙ですが、使い方によっては便利なので紹介します。
作り方は簡単です。スニペット化したいコードを選択した状態で右クリックし、Create Code Snippetを選択するだけです。
例えば、ここではexecute()
関数を中身も含めてスニペット化しています。自動生成でよくある要件に、一部コードは入力に応じて可変にしたいというのがあるでしょう。
それはできないのですが、Placeholderをうまく使うと変更しなければならない箇所を指定できます。PlaceholderはXcode上で<# #>
と記述すると表示できます。
表示は以下のようになります。TabでPlaceholder領域へ飛ぶことができて、ちょっとした定型コードを書くときに便利です。
普段はよく使う定型コードを都度スニペット化して業務に活用しています。Yahoo! JAPANアプリのように巨大なプロジェクトでは、用意されたパーツをおまじない的に組み合わせて新しい機能を作るというケースが多々ありますが、そんなときに役立てています。
この機能は複数人で共有できます。例えば私の作っているライブラリでは使い方をCode Snippetとして配布しています。
- PagingKit: Code Snippet(外部サイト)
Pre(Post)-actions, Run Script
続いて、ビルド時やその前後で実行させる方法として、Pre(Post)-actions, Run Scriptを紹介します。この方法が一番有名だと思います。
冒頭でご紹介したような有名なツールは、Run Scriptでコード生成させることが多いです。Yahoo! JAPANアプリではSwiftGen(外部サイト)を使ってAsset Catalogの要素の型を自動生成しています。
Run Scriptの設定はXcode上でプロジェクトファイル内Build Phasesの「+」から「New Run Script Phase」にて追加できます。普段ターミナルで実行しているコマンドをその中で実行できます。入出力も自由にできるため、かなり自由度の高い方法です。
ここでは細かい実装の説明は省略します。以下のWWDCのセッションで、Run Scriptに触れられているのでご覧ください。
自動生成ツールの他にも、Yahoo! JAPANアプリではStoryboard・XIBファイルのバリデータをRun Script内に仕込んでいます。そういったコードチェック用途として一般的に使われているものでは、SwiftLint(外部サイト)が有名です。
そのように、なんでもできるRun Scriptで十分じゃないかと思われる方も多いかもしれません。しかし、一つデメリットがあります。動かすためにはビルドを実行しなければいけないという点です。
業務で扱うような巨大プロジェクトではコード生成の度にビルドを回すのは面倒でしょう。そうであればいっそのことターミナルで実行した方が早いです。
もちろんそれが一番リーズナブルであれば、ターミナルでの都度実行でいいと思います。ただし、工夫することでRun Script上でも毎回プロジェクトをビルドさせずに済む方法があります。
どうするかというと、スクリプトを回すための専用のターゲットを作ってしまうというやり方です。
ターゲットに関する技術詳細は以下の資料をご覧ください。
- Working with Targets(外部サイト)
プロジェクトファイル上で新規でターゲットの作成を行うと、iOS・macOSなどのタブ選択画面が表示されます。そこで「Other」のタブを選ぶと、Aggregateというターゲットが存在しているので選択してください。
Aggregateターゲットは、カスタムスクリプトによる処理で複数のターゲットをまとめて処理するために使われます。これで単純にRun Scriptを実行するだけのターゲットを作ることができます。都度プロジェクト全体のビルドを走らせる必要はなくなります。
Run Script以外にもPre(Post)-actionsがあります。ビルドプロセスの中にカスタムスクリプトを仕込む別の方法です。ビルド中ではなくビルド前後に実行されるため、動くタイミングを固定したい場合には有効です。
Appleの公式ドキュメントでは以下のような活用例が紹介されています。
Yahoo! JAPANアプリではPre(Post)-actionsでコード動生成は行ってないのですが、ビルド時間の収集に使用しています。Post-actionsで社内開発者の手元ビルド時間を計測し、ログサーバーへ貯めてグラフ化しています。具体的なビルド時間の取得には以下のOSSを活用しています。
- XCLogParser(外部サイト)
ビルド時に生成されるxcactivitylog
を使っているのがポイントです。このツールを使うとそれが比較的楽にできます。
そうやって各開発者の手元でしかわからなかったビルド時間の実態を可視化し、改善提案へ繋げています。
まとめ
Xcodeを使ったコード自動生成方法について、Yahoo! JAPANアプリで使用しているものをいくつか紹介しました。それぞれ使い所や一長一短があります。紹介した方法がみなさんの日々の定型業務の効率化につながればと思います。
こちらの記事のご感想を聞かせください。
- 学びがある
- わかりやすい
- 新しい視点
ご感想ありがとうございました