[iOS]リソースの管理・利用を型安全にするためにSwiftGenを導入する

毎回のビルド時の負担を軽減したくてSwiftGenを導入してみました。
2020.11.10

新規でアプリを作ることになり、プロジェクトのリソースに対応したSwiftコードを自動生成して型安全な利用ができるようになるツールとしてR.SwiftではなくSwiftGenを使ってみたかったので導入してみました。

SwiftGenについて

SwiftGenの他のコードジェネレータと比べて差分を見てコード生成する点や再ビルドが不要な所、生成先及び生成元のパスやリソースの任意の選択等設定が柔軟にできる点が優位な点だと思います。ただ、R.Swiftより少し導入が面倒に感じました。それでもそれを上回るメリットを感じているので今回は過程を記事にしたいと思います。

SwiftGen/SwiftGen: The Swift code generator for your assets, storyboards, Localizable.strings, … — Get rid of all String-based APIs!

インストール

インストール方法は複数あります。zipでダウンロードしたりCocoaPodsなど様々ですが、現在取り組んでいるプロジェクトではMintを導入していますのでMint経由でインスールすることにしました。

Mintでのインストールを行いたい場合6.0以上が条件です。Mintfileに以下のように記載します。

SwiftGen/SwiftGen@6.4.0

mint bootstrapでインストールします。

設定ファイル

SwiftGenはCLIツールとして提供されていて、設定ファイルを使用してパースする必要のある入力ファイルの種類に応じてパーサとそのパラメータを定義します。swiftgen.yamlを定義してプロジェクトのルートディレクトリに設置します。

xcassets:
  inputs:
    - Sources/Resources/Images.xcassets
    - Sources/Resources/Colors.xcassets
    - Sources/Resources/Assets.xcassets
  outputs:
    - templateName: swift5
      output: Sources/Generated/Assets.swift
strings:
  inputs:
    - Sourcese/Resources/Base.lproj
  filter: .+\.strings$
  outputs:
    - templateName: structured-swift5
      output: Sources/Generated/Strings+Generated.swift

static methodで定義されているので

L10n.Greeting.hello

のように使います。

swiftgen config initで設定ファイルを作成できます。設定ファイルの記述が済んだらswiftgen config lintで設定ファイルの検証が行えます。

実行結果を載せますがmint経由で実行しているのでmint runが付いていますが他の環境であれば不要です。

❯ mint run swiftgen config lint
Linting swiftgen.yml
> Common parent directory used for all input paths:  <none>
> Common parent directory used for all output paths: <none>
> 1 entry for command strings:
 $ swiftgen strings --templateName structured-swift5 --output ProjectName/Generated/Strings+Generated.swift --filter .+\.strings$ ProjectName/Resources/ja.lproj
> 1 entry for command xcassets:
 $ swiftgen xcassets --templateName swift5 --output ProjectName/Generated/Assets.swift ProjectName/Resources/Colors.xcassets ProjectName/Resources/Assets.xcassets

設定ファイルでなく直接オプションや引数に適切な値を渡すことでswiftgen runコマンドでファイル生成できますが、プロジェクトでは設定ファイルを使うケースの方が多いと思うので子細は説明しません。気になる人はドキュメントのConfiguration fileの節を参照されてください。

templateNameについて

SwiftGenはSwift製のStencilというテンプレート言語を採用しています。なので使用しているSwiftのバージョンに合ったテンプレートを選ぶことができます。この任意のバージョンを指定するために使用するのがtempleteNameです。

SwiftGenにはそれぞれのパーサ用のテンプレートがバンドルされているて通常はそれを使用するだけですが、テンプレートが使用するプロジェクトに合わない時には独自のテンプレートを作成できます。今回は新規プロジェクトへの導入ということで提供されているテンプレートを使いました。

templateName出力オプションを使って使用するテンプレートの名前を指定するだけです。しかし、バンドルされているテンプレートがあなたのコーディング規約やニーズに合わない場合は、独自のテンプレートを作成することもできます。SwiftGenのテンプレートカスタマイズのためのドキュメントも提供されています。

SwiftGen/SwiftGen

提供されているパーサーは以下です。

  • Core Data
  • Fonts
  • Asset Catalog
  • Colors
  • Strings
  • Interface Builder
  • JSON, YAML
  • Plists

Build Phases

自動生成されるファイルはGitの管理から外していて、ビルド時に生成されるようにしたいのでBuild Phasesに追加しました。Compile sourcesでターゲットを構築する過程でどのソースファイルをコンパイルすべきかをコンパイラに知らせますがその時点で生成されたファイルがないと困るのでRun SwiftGenというSwiftGenを実行するスクリプトをCompile sourcesより前に実行するようにしました。

https://i.gyazo.com/4e0f92a1effcd8198d172aaed14dd312.png

mintを使用しているのでSDKROOTのunsetやmint経由で呼び出したりしていますが、プロジェクトによって書き換えてください。

XcodeのBuild Phasesでmintを実行する時の注意点 - Qiita

unset SDKROOT
if which mint >/dev/null; then
  mint run swiftgen
else
  echo 'warning: Mint not installed. Please install mint from https://github.com/yonaskolb/Mint'
fi

Xibについて

Assets CatalogsやColors、Localizable.stringsに加えてInterface BuilderなどもSwiftGenに対応しているのですがxibは対応していません。既存でなければ個人的にStoryboardはあまり使うつもりがないのでxibを使いたいです。Reusableを使用するとXibでもStringベースのAPIから型安全なAPIに変更できます。

薄いライブラリだったのと、xib経由の初期化の型安全化はNibInstantiatable というprotocolを実装して使うことが多かったので今回は不要と判断して採用しませんでした。

このテーマは国内でも何度か記事になっているのとSwiftGenの説明とは関係がないので説明を譲ります。

まとめ

R.SwiftがあるのにSwiftGenが台頭して気になっていました。案件では既存でR.Swiftが導入されていたのでSwiftGenを業務で使う機会がなかったのですが、運良く仕事で使う機会に恵まれたので使ってみたという記事でした。

不要ならビルドが走らない以外にも柔軟性などメリットがあることが知れて良かったです。

その他記事中で紹介しなかったものの参考にした記事

プロダクトにSwiftGenを導入してタイポ0の世界を作る | Engineers' Blog

SwiftGenの使い方を調べた - enomoto blog