SwiftGen

# SwiftGen とは

[SwiftGen](https://github.com/AliSoftware/SwiftGen) は、iOS / macOS アプリ開発の補助ツールです。リソース（アプリ内の画像やテキストなど）の扱いが楽になるソースコードを自動生成してくれます。（なお、同様のツールに、（R.java 風の）R.swift や rdotm などがあります）

例えば、以下のようなことができます。

```swift
// ツールなしの場合
let image = UIImage(named: "banana")

// SwiftGen を使った場合
let image = UIImage(asset: .Banana)
```

リソースの名前を文字列で書かなくてはならなかったところが、enum 定義値で書けるのがポイントです。

## 対応しているリソース

以下のリソースに対応しています。

* 画像
* 文字列（ローカライズ）
* Storyboard
* 色
* フォント

## 使い方

* `swiftgen images DIR`
* `swiftgen strings FILE`
* `swiftgen storyboards DIR`
* `swiftgen colors FILE`
* `swiftgen fonts DIR`

それぞれのコマンドを実行すると、リソースに応じた Swift ソースコードが出力されます。

## 入力データ

* 画像 : `*.xcassets`
* 文字列 : `*.strings`
* Storyboard : `*.storyboard`
* 色 : 色定義ファイル（JSON、XML、`*.clr` など）
* フォント : `*.ttf`, `*.otf` など

画像、文字列、Storyboard は、通常のアプリ開発で使われるリソースそのものを SwiftGen に読み込ませることができます。

色は、色定義ファイルを別途用意する必要があります。といっても、色名と RGB 値を羅列したシンプルな JSON ファイルを用意すればすみます。

# 利用側のコード

SwiftGen が生成したコードをアプリ内で利用する方法について述べます。

## Images

2種類の書き方があります。

```swift
let image = UIImage(asset: .Banana)
let image = Asset.Banana.image
```

なお、macOS の場合は `UIImage` でなく `NSImage` となります。

## Strings

2種類の書き方があります。

```swift
let title = tr(.AlertTitle)
let title = L10n.AlertTitle.string
```

## Storyboards

```swift
// ViewController
let initialVC = StoryboardScene.Main.initialViewController()
let loginVC = StoryboardScene.Main.LoginViewController()

// Segue
performSegue(StoryboardSegue.Main.CreateAccount)
```

上記の `initialViewController()` はちゃんと Storyboard を読んで適切な型で返してくれます（`UIStoryboard` の `instantiateInitialViewController()` は `UIViewController` を返すので、自分で適切な型にキャストしなくてはならないですよね）。

なお、macOS の場合は、swiftgen のオプション `--template storyboards-osx-default` をつけてコードを生成します。`UIStoryboard` でなく `NSStoryboard` に対応したコードが生成されます。

## Colors

```swift
let titleColor = UIColor(named: .Title)
let titleColor = ColorName.Title.color
```

なお、macOS の場合は `UIColor` でなく `NSColor` となります。

## メリット

SwiftGen を使うと、リソースの指定に文字列ではなく enum 定義値を使います。このため、リソース指定のミスがなくなるというメリットがあります（記述が間違っていたらビルド時にエラーになる）。また、補完入力もでき、コードが書きやすくなります。

また、`Localizable.strings` を書くのは結構面倒なものだと思います。しかし、SwiftGen のおかげで、`Localizable.strings` を書けばコードが書きやすくなるのですから、やっておこうという気持ちになります。このような、心理的負担の軽減はなかなか馬鹿にならないメリットだと思います。

# 生成コードを見てみる

SwiftGen が生成するコードがどんなものなのか、簡単に確認しておきます。以下、Images を見てみます。

文字列が enum の `case` として定義されているのがわかります。

```swift
enum Asset: String {
    case Banana = "banana"
    case Apple = "apple"
}
```

その enum を使って `init` が行えるように extension が書かれています。

```swift
extension Image {
    convenience init!(asset: Asset) {
        self.init(named: asset.rawValue)
    }
}

// 利用方法
let image = UIImage(asset: .Banana)
```

また、enum のプロパティも定義されています。中では先ほどの `init` を呼んでいます。

```swift
enum Asset: String {
    var image: Image {
        return Image(asset: self)
    }
}

// 利用方法
let image = Asset.Banana.image
```

特に難しいことはなく、シンプルなコードを生成してくれていることが分かります。

# コード生成テンプレート

生成するコードはカスタマイズできます。生成コードのテンプレートを用意すれば良いです。

標準でいくつかのテンプレートが用意されています。`swiftgen templates list` で一覧できます。

```
colors:
  custom:
  bundled:
   - default
   - rawValue
   - swift3
images:
  custom:
  bundled:
   - allvalues
   - default
   - swift3
storyboards:
  custom:
  bundled:
   - default
   - lowercase
   - osx-default
   - osx-lowercase
   - swift3
   - uppercase
strings:
  custom:
  bundled:
   - default
   - dot-syntax-swift3
   - dot-syntax
   - genstrings
   - structured
   - swift3
fonts:
  custom:
  bundled:
   - default
   - swift3
```

テンプレートは、[Stencil](https://github.com/kylef/Stencil)（Mustache の Swift 版のようなもの）の形式で記述します。例えば次のような内容です。

```
enum {{enumName}}: String {
    {% for image in images %}
    case {{image|swiftIdentifier}} = "{{image}}"
    {% endfor %}
}
```

より詳細は、[Customize SwiftGen templates](https://github.com/AliSoftware/SwiftGen/blob/master/documentation/Templates.md) を参照してください。

# Tips

## Tips (1) : ビルド時に SwiftGen を実行する

リソース変更のたびに SwiftGen を実行するのは面倒で、忘れがちです。

そこで、Xcode の Build Phase で実行してしまう（ビルド時にコード自動生成もついでに行う）のが良いと思います。具体例が [Integrate SwiftGen in an xcodeproj](https://github.com/AliSoftware/SwiftGen/wiki/Integrate-SwiftGen-in-an-xcodeproj) に書かれています。

## Tips (2) : コマンドをプロジェクトに追加する

SwiftGen を使う場合は、通常は Homebrew などでインストールする必要があります。

しかし、CocoaPods で SwiftGen を追加すると、`swiftgen` コマンドがプロジェクト内に追加されます（CocoaPods は一般にはライブラリを追加するツールですが、ここではライブラリは追加されずコマンドが追加されます）。Xcode の Build Phase で実行する場合に、コマンドが確実に存在するので便利です。

# まとめ

* リソースからコードを自動生成してくれる
* リソース指定文字列のミスがなくなり、コードが書きやすくなる
* 生成コードは必要に応じてカスタマイズできる
* ビルド時に自動生成をしておくと便利

なお、この記事は、[関モバ #17](http://kanmoba.connpass.com/event/37712/) で行った LT（[SwiftGen // SpeakerDeck](https://speakerdeck.com/usamik26/swiftgen)）をベースに作成しました。