はじめに
SwiftGenをSwiftPMで使う方法を紹介します。
「SwiftGen」とは?
リソース(ローカライズされた文字列や画像など)に対してSwiftのコードを生成し、型安全にアクセスするためのCLIツールです。
R.swiftと競合するパッケージです。
R.swiftとの比較
R.swiftとSwiftGenを比較します。
両方とも素晴らしいパッケージなので、どちらを使うか迷います。
- CLIツールのみで完結する
R.swiftはCLIツールとライブラリの両方が必要なのに対し、SwiftGenはCLIツールのみで完結する - 設定ファイルが必要
R.swiftは設定ファイルが不要なのに対し、SwiftGenは作成する必要がある
個人的な感覚は以下の通りです。
- 簡単に使いたい → R.swift
- ライブラリを増やしたくない、またはカスタマイズしたい → SwiftGen
SwiftGen導入のメリット
R.swiftと同様なので、以下の記事をご参照ください。
環境
- OS:macOS Monterey 12.5.1
- Xcode:14.2 (14C18)
- Swift:5.7.2
- Swift Package Manager:5.7.1
- SwiftGen:6.6.2
セットアップ方法
SwiftGenプラグインのインストール
SwiftGenは公式でプラグインが用意されています。
CLIツールとプラグインでリポジトリが分かれているのが特徴です。
プラグインはSwiftGenのArtifact bundle(≒ビルド済みバイナリ)にのみ依存していることが明確になっており、他のパッケージと依存が被らないので使いやすいです。
インストールは Package.swift
の dependencies:
に追記するのみです。
dependencies: [
+ .package(url: "https://github.com/SwiftGen/SwiftGenPlugin", from: "6.6.2"),
],
ビルド時にリソースファイルを生成するようにする
Package.swift
のリソースが含まれているターゲットにプラグインを追加するだけで、ビルド時に自動でリソースファイルが生成されます。
targets: [
.target(
// ...
plugins: [
+ .plugin(name: "SwiftGenPlugin", package: "SwiftGenPlugin"),
]),
]
リソースファイルを生成したい全ターゲットに追加する必要があります。
プラグインの追加が手間なので、リソース用のターゲット(モジュール)を作り、リソースを1つのモジュールで一元管理するのもありだと思います。
ただ私は今のところ、必要なリソースを必要なモジュールのみに含めるのが好みです。
設定ファイルを作成する
設定ファイルは各ターゲットのパスに swiftgen.yml
を追加すると、自動的に読み込まれます。
以下は設定ファイルの一例です。
input_dir: Resources
output_dir: ${DERIVED_SOURCES_DIR}
strings:
inputs:
- ja.lproj/Localizable.strings
outputs:
templateName: structured-swift5
output: Strings.swift
xcassets:
inputs:
- Assets.xcassets
outputs:
templateName: swift5
output: Assets.swift
Swift Package Manager 5.7.1時点で、Build Tool Pluginは DerivedData
( ${DERIVED_SOURCES_DIR}
) にしかファイルを生成できません。
そのため output_dir
は変更できません。
1つの設定ファイルに複数のターゲットの設定をまとめて書くこともできます。
私はターゲットを削除するときに設定を消し忘れるのを防ぐため、ターゲットごとに設定を書くのが好みです。
設定ファイルの書き方の詳細は、公式ドキュメントをご参照ください。
使い方
ローカライズされた文字列
structured-swift5
テンプレートを使う場合、 L10n.○○
でローカライズされた文字列にアクセスできます。
- String(localized: "Copy Sakatsu text", bundle: .module)
+ L10n.copySakatsuText
アセット
swift5
テンプレートを使う場合、 Asset.○○.image
で画像に UIImage
としてアクセスできます。
- Image("foo_and_bar", bundle: .module)
+ Image(uiImage: Asset.fooAndBar.image)
Asset.○○.swiftUIImage
で SwiftUI.Image
としてアクセスできるので、SwiftUIの場合はこちらのほうがスマートです。
- Image(uiImage: Asset.fooAndBar.image)
+ Asset.fooAndBar.swiftUIImage
おまけ: プラグインを自作する
ソースを見ればわかりますが、プラグインはCLIツールをラップした薄いファイルです。
Build Tool Pluginの場合、CLIツールに引数を一切渡せないため、カスタマイズしたい場合はプラグインを自作する必要があります。
SwiftGenはArtifact bundleを提供しており、プラグインも100行ないため、簡単に自作できます。
プラグインを自作する方法は、以下の記事がわかりやすいです。
おまけ: マルチモジュールにおけるリソースの管理方法
SwiftPMでモジュール分割するとき、リソースをどう管理するかの考えを、以下のツイートのツリーでまとめています。
リソースを1つのモジュールで一元管理する場合、アクセス修飾子を internal
から public
にする必要があります。
swiftgen.yml
から簡単に変更できます。
input_dir: Resources
output_dir: ${DERIVED_SOURCES_DIR}
strings:
inputs:
- ja.lproj/Localizable.strings
outputs:
templateName: structured-swift5
output: Strings.swift
+ params:
+ publicAccess: true
xcassets:
inputs:
- Assets.xcassets
outputs:
templateName: swift5
output: Assets.swift
+ params:
+ publicAccess: true
おわりに
Swift Package上で、ローカライズされた文字列をスッキリかつ安全にアクセスできるようになりました
SwiftPMのプラグインは便利なので、他のCLIツールでも積極的に使っていきたいです。
参考リンク
- Setup SwiftGen plugin by uhooi · Pull Request #126 · uhooi/Loki
- SwiftGen/structured-swift5.md at 1cf6d7eebd70c2157f69d5a991bc57c1ef182ed1 · SwiftGen/SwiftGen
- SwiftGen/README.md at 1cf6d7eebd70c2157f69d5a991bc57c1ef182ed1 · SwiftGen/SwiftGen
- Make output file scoped to Public? · Issue #837 · SwiftGen/SwiftGen
- How to use SwiftGen for internal framework · Discussion #833 · SwiftGen/SwiftGen