• 35
    いいね
  • 0
    コメント

SwiftGen とは

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

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

// ツールなしの場合
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種類の書き方があります。

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

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

Strings

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

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

Storyboards

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

// Segue
performSegue(StoryboardSegue.Main.CreateAccount)

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

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

Colors

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

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

メリット

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

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

生成コードを見てみる

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

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

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

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

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

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

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

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(Mustache の Swift 版のようなもの)の形式で記述します。例えば次のような内容です。

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

より詳細は、Customize SwiftGen templates を参照してください。

Tips

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

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

そこで、Xcode の Build Phase で実行してしまう(ビルド時にコード自動生成もついでに行う)のが良いと思います。具体例が Integrate SwiftGen in an xcodeproj に書かれています。

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

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

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

まとめ

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

なお、この記事は、関モバ #17 で行った LT(SwiftGen // SpeakerDeck)をベースに作成しました。