Edited at
iRidgeDay 14

SwiftGenを導入して無駄なビルド負担を低減する

14日目、今年も残るところ17日ですね⛄️

今年初めて導入した SwiftGen について紹介します。


0. Abstract

「ジェーーンは、すごく賢い!」

そんなこんな評判を耳にして、今年の開発プロジェクトに導入しました。

事前に下調べをして導入しようと早速ググっみたものの、

エンジニアブログなどの日本語での記事が想像していたよりも少なく、

あまり日本では導入していないのか、それとも他に何か理由でもあるのか...。

実際にGoogleでの検索結果は

SwiftGen
R.Swift

Googleでのヒット数
21,600 件
487,000,000 件

R.Swiftと比較して圧倒的に情報量が非常に少ない... 😭

一方、githubのコミット状況はどうかというと、こんな感じです。

SwiftGen
R.Swift

contributers
53
30

commits
1421
1001

releases
46
66

branches
6
5

(12/09/2018時点 by github)

コミッターの数や開発中のブランチでみると、非常に勢いを感じます。

(さすがに R.Swift ではリリース回数では及びませんね...)


1. SwiftGenとは?

Swiftプロジェクトで使用される画像イメージやフォント, segue等のようなリソース名を自動的に生成し、型付を行ってくれるライブラリです。

基本的な使用目的としては, R.Swiftと同じです。


  • 文字列を利用する際に余計なTypeを回避できる

  • 自動補完機能を利用できる

  • 存在しない asset を使用してしまうリスクと回避できる

  • コンパイル時にこれら全てをチェックできる


2. R.Swiftと何が違うか

SwiftGenの実体はライブラリではなく、コマンドライン・ツールです。

この点が R.Swift と大きく異なります。

メリット

* 開発の活発頻度が高い

* 定期的なアップデートが望めそう

* ライブラリのインポートが必要ない

* 生成するリソース種別を取捨選択できる

* R.Swiftのように再ビルドが走らない

デメリット


  • 導入例が多くない

  • 日本語翻訳されたドキュメントや説明が少ない

  • R.Swiftよりも導入が手間(後述)

  • stringdictに非対応

R.Swiftとの比較

有名なリソース管理ライブラリとして、R.Swift がよく知られています。

SwiftGen
R.Swift

インストール
CocoaPods, Zip, Homebrew, Mint, Compile from source
CocoaPods, Zip, SPM

導入
やや面倒
簡単

制御
不要な機能は disable
全部

ビルド
クラスがリソースごとに分かれているため、毎回全ビルドが走ることはない
リソースファイルに増減がある度に R.generated.swift の全ビルドが走るので、大規模プロジェクトでは時間がかかる

対象
Image, Font, Color, Storyboards, Localizable.strings
Image, Font, Color, Storyboards, Localizable.strings, Segues, Nibs, Reusable cells

もしこれから導入を考える場合には、こちら Medium も参考になります。


3.導入方法

早速、導入してみましょう。


1. インストール


CocoaPodsから

Podfileに pod 'SwiftGen' を記述し、pod installを実行する。


Podfile

target 'アプリ名' do

# Comment this line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!

# Pods for SampleLocation
pod 'SwiftGen'

end


CocoaPods で SwiftGen を追加すると、swiftgen コマンドがプロジェクト内に追加されます(CocoaPods は一般にはライブラリを追加するツールですが、ここではライブラリではなく Pods/ にバイナリが追加されるのみです)。


Run_Script_Phase

"$PODS_ROOT"/SwiftGen/bin/swiftgen


ビルド時にswiftgenコマンドが実行されます。


Homebrewから

Homebrewでインストーする場合、システム全体にインストールされます。

同じマシンならば、同じバージョンのswiftgenで処理されます。

もしもチーム開発する場合、必ずバージョンを揃えておく必要があります。

$ brew update

$ brew install swiftgen


2. 設定ファイルの作成

プロジェクトファイル直下に swiftgen.yml を作成します。

SwiftGen Configuration File


例.swiftgen.yml

// 画像リソース用

xcassets:
paths: "$PROJECT_NAME"/Resources/Assets.xcassets # 入力パス
templateName: swift4                   # テンプレート指定
output: "$PROJECT_NAME"/Resources/Generated/DefaultAsset.swift # 生成パス

// ローカライズ用
strings:
paths: "$PROJECT_NAME"/Resources/Strings/en.lproj/Localizable.strings
templateName: structured-swift4
output: "$PROJECT_NAME"/Resources/Generated/Localized.swift

// Storyboard用
storyboards:
paths: "$PROJECT_NAME"/Scenes/Storyboards/Base.lproj
templateName: swift4
output: "$PROJECT_NAME"/Resources/Generated/Storyboards.swift

...



  • 入力パス: 読み取り対象(リソース)のパス名をします。

  • 出力パス: 自動生成されたソースファイルを出力するパスを指定します。

  • templateName: Swiftバージョンに一致するテンプレートを指定します。

templateNameでは、生成コードに関するテンプレートを指定できます。

設定可能なテンプレートを確認するには、以下のコマンドでを確認できます。

生成されるソースコードの構造(structured or flat)も選べるようです。

    $ swiftgen templates list


Templete

    xcassets:

custom:
bundled:
- swift3
- swift2
- swift4
colors:
custom:
bundled:
- literals-swift4
- swift3
- swift2
- literals-swift3
- swift4
strings:
custom:
bundled:
- flat-swift4
- structured-swift4
- structured-swift2
- flat-swift3
- flat-swift2
- structured-swift3
storyboards:
custom:
bundled:
- swift3
- swift2
- swift4
fonts:
custom:
bundled:
- swift3
- swift2
- swift4
---

テンプレートは独自に定義することも可能です。

より詳しい情報はこちらを参照してみて下さい。


3. 2をチェックします。

 swiftgen config lint を叩いてみましょう。

$ swiftgen config lint

Linting swiftgen.yml
> Common parent directory used for all input paths: {_PROJECT_NAME_}/Others
> Common parent directory used for all output paths: {_PROJECT_NAME_}/GeneratedCodes/
> 1 entry for command strings:
$ swiftgen strings -t structured-swift4 -o {_PROJECT_NAME_}/GeneratedCodes/L10n-Constants.swift Demo-App/Others/ja.lproj/Localizable.strings


4.swiftgenコマンドを実行する

$ swiftgen config run

or

$ swiftgen   # 省略形

swiftgenコマンドが正常に通った場合、出力先のディレクトリにはファイルが生成されます。

これで、自動補完ができるようになります。


4. その他


インテグレート

Xcodeに Run Script Phase を追加し,

ビルド実行をトリガーにして自動でコード生成を行います。

(※CocoaPodsからインストールすれば、この設定は必要ありません)


  1. 左上のProject Navigatorを選ぶ

  2. アプリのTargetを選ぶ

  3. "Build Phases" タブに移動する

  4. 上部の左角付近のボタン「+」をクリックし, "New Run Script Phase" を選ぶ

  5. 以下のようにスクリプトを記述する


  • ケース1. CocoaPods

    "$PODS_ROOT"/SwiftGen/bin/swiftgen


  • ケース2. ZIP解凍file

    "$PROJECT_ROOT"/SwiftGen/bin/swiftgen


  • ケース3. Homebrew

    if which swiftgen >/dev/null; then

swiftgen
else
echo "warning: SwiftGen not installed, download it from https://github.com/SwiftGen/SwiftGen"
fi


ディレクトリ単位でパス指定

swiftgen.ymlからの相対パスで, .stringsの相対パス, Swiftファイルの出力先を設定することもできます。

    # 入力用の親ディレクトリ

input_dir: Demo-App/Others

# 出力用の親ディレクトリ
output_dir: Demo-App/GeneratedCodes/

# 文字列用の設定
strings:
paths: ja.lproj/Localizable.strings
templateName: structured-swift4
output: L10n-Constants.swift

SwiftGen Configuration File

Localizing iOS app with SwiftGen


5. さいごに

実際に導入してみての感触として、ビルドを必須としないところがいい感じです。どちらが優秀かという点については、CocoaPods vs Carthage論争と同様、最終的に導入コスト、環境依存性、カスタマイズ性などを総合して判断するのが一番良いかなと思います。

私はプロジェクトファイル直下で Rakefile を定義することがあるので、

以下のように swiftgen コマンドも追加してみました。


例.Rakefile

#xcprojectのファイル名

PROJECT_NAME = '...'

# xcworkspaceのファイル名
WORKSPACE_NAME = '....'

desc 'Xcodeを開く'
task :open do
sh 'open', "#{WORKSPACE_NAME}.xcworkspace"
end

desc 'Carthageのビルド'
task :carthage do
sh "carthage update --platform iOS"
end

desc 'Podsの準備'
task :pod do
sh "bundle install --path vendor/bundler"
sh "bundle exec pod install"
end

...

desc 'SwiftGenの実行'
task :swiftgen do
sh 'swiftgen config lint'
sh 'swiftgen config run'
end


画像やStoryboard、stringsファイルを追加・変更したとき、

$rake swiftgen

をコマンドで叩けるようにしています。