カテゴリー: iOS

SwiftGenを使ってみた

はじめに

こんにちは。カイザーです。Swiftでのコードジェネレータでは、SwiftGenやRSwiftが有名ですね。
今回は、SwiftGenを紹介します。

SwiftGenとは、stringsやStoryboard、Asset Catalog等のリソースから、Swiftファイルをジェネレートする、CLIツールです。
iOSでのリソース使用にありがちな、文字列での名前指定では無く、ジェネレートされた定数定義や構造体を使用することにより、typoを無くすことができます。

今回は、簡単なスライドショーアプリをサンプルに、SwiftGenを使っていきたいと思います。

SwiftGenの特徴

先に、SwiftGenの特徴を説明しておきましょう。

コードジェネレートするリソースを選択することができる

様々なリソースの中から、必要な種類、ファイル、ディレクトリなどに絞り込む設定が可能です。
そのため、コードジェネレートに余計な時間はかかりません。
(R.swiftでは対応するすべてのリソースが自動的に含まれます。)

コードジェネレートするのにビルドが必要ない

自前でリソースファイルをパースし、コードジェネレートします。そのため、ビルドは発生しません。

設定ファイルベースである

swiftgen.ymlファイルをプロジェクトファイルと同じ階層に配置し、設定を記載することができます。
この設定ファイルさえGitで共有すれば、誰でも同じルールでコードがジェネレートされます。

インストール

Cocoapods, Homebrew, Mintからインストール可能ですが、今回はCocoapodsを使用します。プロジェクトごとに、SwiftGenのバージョンを管理したいためです。

Podfileにpod 'SwiftGen'を追記し、$ pod installすれば、インストール完了です。
利用するには、プロジェクトルートまで移動し、$ Pods/SwiftGen/bin/swiftgenで実行可能です。

Localizable.strings

多言語アプリでは必須のファイルですね。

swiftgen.ymlでの設定

以下の設定を行います。

strings:
  inputs: swiftgen-ex/Base.lproj/Localizable.strings
  outputs:
    - templateName: structured-swift4
      output: swiftgen-ex/Generated/strings.swift

なお swiftgen-exはサンプルプロジェクト名なので、皆さんのプロジェクト名に読み替えてください。

strings

1番初めのstringsはリソースの種類を表します。SwiftGenが対応しているもののみ、指定可能です。

inputs

inputsは、リソースファイルそのものを示します。ファイル単体を指定することもできますし、ディレクトリを指定することもできます。
指定は単数でも、複数でも可能です。
ディレクトリの場合、その中の全てのファイルが対象となりますが、別途filterを設定することで、正規表現でファイル名を指定することもできます。(Interface Builderで説明します)

outputs

outputsはコードジェネレータの出力方法や、出力先を設定します。

テンプレート

SwiftGenでは「テンプレート」によって、ジェネレートされたSwiftコードの構造が決まります。それを指定するのがtemplateNameです。
stringsでは、structured-swift4flat-swift4が使用可能です。

structured-swift4

stringsファイルで、キーをドット区切りにすると、その指定に沿って構造化された定数がジェネレートされます。(実際はenumです)
例えば、以下のようなstringsファイルを作ると

"cities.italy.name" = "Italy";
"cities.london.name" = "London";
"cities.paris.name" = "Paris";

Swift側からは、以下のように呼び出せます。

L10n.Cities.London.name
L10n.Cities.Paris.name
L10n.Cities.Italy.name

さらに、L10nでピンと来たかもしれませんが、この呼び出しによりLocalizable.stringsでの多言語も反映されます。

flat-swift4

今回は使用しませんが、structured-swift4とは対象的に、構造的にならず、フラットな構造でコードジェネレートされます。

output

ジェネレートされたSwiftファイルの出力先です。今回はGeneratedディレクトリ配下にします。
このディレクトリ配下は、コンフリクト防止のため、gitignoreしておくと良いでしょう。

コードジェネレート

リソースファイルの準備と、swiftgen.ymlの編集が完了したら、プロジェクトルートで$ Pods/SwiftGen/bin/swiftgenを実行します。
すると、outputで指定したディレクトリにSwiftファイルが出てきます。
これらをプロジェクトに追加し、使用します。

Interface Builder

Storyboardには様々な「Identifier」という設定項目があり、全て文字列のため、誰しも間違えたことはあるでしょう。SwiftGenではStoryboardとSegueを定数化することができます。

swiftgen.ymlでの設定

以下を追記します。

ib:
  inputs: swiftgen-ex/Base.lproj
  filter: .+\.storyboard$
  outputs:
    - templateName: scenes-swift4
      output: swiftgen-ex/Generated/Storyboard Scenes.swift
    - templateName: segues-swift4
      output: swiftgen-ex/GEnerated/Storyboard Segues.swift

1番初めはibとなります。

inputs/filter

filterでは、inputsのディレクトリの内容を、正規表現で絞り込むことができます。今回は、拡張子がstoryboardのものを絞り込んでいます。

outputs

今回はoutputsが2つあります。scenes-swift4はStoryboardや画面、segues-swift4はSegueを定数化します。

コードジェネレート

Storyboard

StoryboardのViewControllerなどをインスタンス化するには、以下のようにします

        // Main.storyboardのInitial Sceneのインスタンスを生成
        let initialViewController = StoryboardScene.Main.initialScene.instantiate()

        // Main.storyboardのIdentifier"viewController"のインスタンスを生成
        let viewController = StoryboardScene.Main.viewController.instantiate()

Storyboardのファイル名や、Identifierを文字列として渡す必要が無いので、すっきりとしています。

Segue

コードからSegueで画面遷移するときに、とても便利です。

// Main.storyboardのSegue Identifier"toPhoto" で遷移する。
perform(segue: StoryboardSegue.Main.toPhoto)

値を渡すときは、これまで通りprepare(for segue:)を実装しますが、Segueの判定にSwiftGenが使えます。

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // UIStoryboardSegueからStoryboardSegue.Mainのインスタンス生成
        switch StoryboardSegue.Main(segue) {
        case .toPhoto?: // "toPhoto" Segueの遷移
            if let photoViewController = segue.destination as? PhotoViewController {
                photoViewController.imageAsset = photos[index].asset
            }
            break
        default:
            break
        }
    }

このコードは、サンプルアプリで写真がタップされたときに、写真画面に遷移する内容です。
残念な点が1つだけあり、destinationのキャストが従来通り発生してしまうことです。ここは、R.Swiftの方が、一枚上手かなと思います。

Asset Catalog

最近のXcodeでは、#imageLeteralなどを使用することによって簡単にAsset Catalogにアクセスできるようになりましたが、SwiftGenでもAsset Catalogの定数化が可能なので、紹介します。
普段のIDEにAppCodeを使用している場合は重宝します。

swiftgen.ymlの設定

以下を追記します。

xcassets:
  inputs:
    - swiftgen-ex/Assets.xcassets
  outputs:
    - templateName: swift4
      output: swiftgen-ex/Generated/assets-images.swift

1番初めはxcassetsとなります。AssetCatalogの設定はシンプルですね。ちなみに、複数のAssetCatalogを使用している場合は、inputsに書き足したり、複数のAssetCatalogが入っているディレクトリごとまとめて指定してしまっても構いません。

コードジェネレート

今回は、Assets.xcassets直下に、”london”, “paris”, “italy” の3つの画像を配置しました。そのため、それぞれの呼び出しは以下となります。

let londonImage = Asset.london.image
let parisImage = Asset.paris.image
let italyImage = Asset.italy.image

これで、UIImageを取得することができます。

その他のリソース

ここで紹介した以外にも、様々なリソースに対応しています。以下にそれぞれのジェネレート仕様が記載されています。
https://github.com/SwiftGen/SwiftGen/tree/master/Documentation/SwiftGenKit%20Contexts

また、出力テンプレート仕様については、以下に記載されています。
https://github.com/SwiftGen/SwiftGen/tree/master/Documentation/templates

さいごに

今回は、SwiftGenを試しましたが、同種であるR.Swiftも調べながら調査しました。どちらにも、長所短所があるため、R.Swiftとの比較記事も書いてみたいと思います!

おすすめ書籍

カイザー

シェア
執筆者:
カイザー
タグ: Swift

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前