49
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

R.swiftとSwiftGenの導入方法とどちらを採用した方がいいのか

Last updated at Posted at 2019-12-17

CA Tech Dojo/Challenge/JOB Advent Calendar 2019の18日目は@ostk0069が書かせていただきます。
次の日、19日目は@hmarfさんです!楽しみにしてます!
自分は、2019年8月にCATechDojo(Kotlin編)に参加させていただいた後、11月にCATechJOBでマッチングエージェントさんでiOSエンジニアとしてインターンをさせていただきました。大変お世話になりました。

はじめに

私は現在進行形で個人アプリの開発をしています。その際に初めはR.swiftを導入していたのですが、途中からSwiftGenへ移行したのでそこでわかった、互いの良い面、悪い面について触れていければと思います。

R.swift、SwiftGenの話はこの記事を見たら理解が十分な状態に仕上げられていると思うのでよかったら最後まで読んでいただけるとありがたいです。

R.swift、SwiftGenとは

これらは主に文字列管理のしやすさのために使用するライブラリです。
文字列管理で一番最初に思い浮かべるのは多言語化対応かと思いますが、それだけではありません。
多言語化対応しないアプリでも導入する価値は十分にあります。UIImageUIColorの指定もコードでtypeSafeで利用することができるので利便性があります。image literalcolor literalで指定したものは次に開いたときには何を指定していたかわからなくなるのでそれが気に入らない方はコードで指定している分、管理しやすいとも言えます。

結論

結論から言うとR.swiftSwiftGen、どちらを導入するか迷っている方で少なくともSwiftGenを導入することを大きなコストと捉えていない方はSwiftGenを導入する方がいいのではという気持ちです。

その理由も含めて、まずは導入方法から色々と説明していこうと思います。

このようなフローで説明していきます。
(飛ばしたい方はこちらから選択して見て行って下さい)

本来であれば比較とどっちを選ぶべきであるかのみでもいいかなと思ったのですが、
導入方法について、紹介したいTipsがあったので導入方法も追加することにしました!

R.swift

導入方法

GitHubに記載されているドキュメントを参照するとCocoaPodsでの導入が推奨されているのでそちらでの方法で説明していきたいと思います。

ここではCocoaPods自体の導入方法は説明しないのでわからない方はこちらからどうぞ。

Podfileへ以下の一行を追加します。

Podfile
pod 'R.swift'

その後、Terminal上で以下のコマンドを叩きます。

pod install

この後、ドキュメントではNew Run Script Phaseを選択したのち、"$PODS_ROOT/R.swift/rswift" generate "$SRCROOT/R.generated.swift"を追加するように書いてあります。

しかし、この記述方法はあまりオススメしません。自動生成されるファイルのR.generated.swiftが自分のプロジェクトファイルのレポジトリ直下に生成されてしまうからです。GitHubでソースコードを管理している人は特にレポジトリ直下に.swiftのファイルが存在するのは結構違和感があるのではないかと思います。なのでNew Run Script Phaseを選択したのち、以下を追加しましょう。

"$PODS_ROOT/R.swift/rswift" generate "$SRCROOT/"プロジェクトファイル"/"hoge"/"fuga"/R.generated.swift"
# プロジェクトファイル以下は自分の好きな場所に追加すればいいと思います

その後、アプリをビルドすることでR.generated.swiftのファイルが上記で指定したディレクトリに自動生成されます。自動生成されたファイルは.projectへの追加は自動でされないので、手動で追加してあげましょう。

その後、自動生成ファイルをgitで管理する意味はないので

.gitignore
*.generated.swift

を追加してあげることでひとまず導入は終了となります。

記述方法のサンプル

これはドキュメントから引っ張ってきたものなんですが、

通常で書く場合

let icon = UIImage(named: "settings-icon")
let font = UIFont(name: "San Francisco", size: 42)
let color = UIColor(named: "indicator highlight")
let viewController = CustomViewController(nibName: "CustomView", bundle: nil)
let string = String(format: NSLocalizedString("welcome.withName"))

R.swiftで記述した場合

let icon = R.image.settingsIcon()
let font = R.font.sanFrancisco(size: 42)
let color = R.color.indicatorHighlight()
let viewController = CustomViewController(nib: R.nib.customView)
let string = R.string.localizable.welcomeWithName("Arthur Dent")

とこのような違いがあります。すべてR.が始まる共通点がありますね。

SwiftGen

導入方法

R.swiftに対して導入はSwiftGenの方が少し複雑です。
まず初めに導入方法を選ぶ必要がある訳ですが、今回はR.swiftのときにCocoaPodsで導入の説明を行ったので揃えた方が違いを理解しやすいと思うのでCocoaPodsを使った場合での説明を行っていこうと思います。

Podfileへ以下の一行を追加します。

Podfile
pod 'SwiftGen'

その後、Terminal上で以下のコマンドを叩きます。

pod install

次に設定ファイルを作成します。このファイルの設定を記述するのがこの導入を難しくしています。

touch swiftgen.yml

作成したswiftgen.ymlを編集していく訳ですが、まず初めにドキュメントに書かれているサンプルを見ていきましょう

swiftgen.yml
strings:
  inputs: Resources/Base.lproj
  filter: .+\.strings$
  outputs:
    - templateName: structured-swift5
      output: Generated/strings.swift
xcassets:
  inputs:
    - Resources/Images.xcassets
    - Resources/MoreImages.xcassets
  outputs:
    - templateName: swift5
      output: Generated/assets-images.swift

これがどのような設定をしているのか説明していきます。

このサンプルでは

  • Base.lprojのファイルからの生成
  • .xcassetsのファイルからの生成

を行っています。もちろん、SwiftGenではそれ以外のStoryboardとかも生成できる訳ですが、それは別途記述が必要です。

さらに具体的にどのように書いているか見ていくと、
inputsoutputsが定義されていて

  • inputsは生成したいファイルの指定
  • outputsは生成したもの出力先

気になるのはfiltertemplateNameというものが存在することです。

filterとは

本来であればinputsの中のファイルの指定はBase.lprojではなく、Base.lproj/Localizable.stringsである方が自然です。しかし、多言語化対応をしているとおそらくLocalizable.stringsは対応している言語分あるはずなので指定が面倒なのでfilter: .+\.strings$と記述してあげることですべてのLocalizable.stringsをinputsの対象に含めることができます

templateNameとは

SwiftGenのなかで指定することのできるtemplateNameは以下です。

  • structured-swift5
  • flat-swift5
  • swift5
  • structured-swift4
  • flat-swift4
  • swift4
  • structured-swift3
  • flat-swift3
  • swift3

特に使用しているSwiftのバージョンに揃える形で対応するといいかと思われます。

stringの指定をするときにはswift4swift3の指定は行えないので注意してください。
flatstructuredを選択するときは構造的にしたいかしたくないかで選びましょう。stringの場合、多言語化対応が多いと思うのでstructuredを推奨します。

この選択肢以外にも自分でよりカスタマイズしたい方は別の方法が存在します。
それはStencilを使うことで実現可能です。元々上記のstructured-swift4flat-swift4Stencilを使って作成されたテンプレートです。気になった方はぜひ見てみてください。(この記事ではこの内容にこれ以上Stencilについては触れません)

記述方法のサンプル

通常で書く場合

let icon = UIImage(asset: Asset.Exotic.icon) 
let displayRegular = UIFont(font: FontFamily.SanFrancisco.regular, size: 42.0)
let title = UIColor(named: .articleBody)
let string = String(format: NSLocalizedString("welcome.with_name"))

SwiftGenで記述した場合

let icon = Asset.Exotic.banana.image
let displayRegular = FontFamily.SFNSDisplay.regular.font(size: 42.0)
let title = ColorName.articleBody.color
let message = L10n.Welcome.withName

R.swiftとSwiftGenの比較

特徴を並べていきます。

R.swift

  • 導入が楽
  • リソースが単一
  • buildするタイミングで毎回自動生成が走る

SwiftGen

  • swiftgen.ymlファイルでカスタマイズできるので拡張性がある
  • リソースが複数
  • 差分を見て生成するかどうかを見てくれる

今回この記事を書くきっかけにもなったのですが、私はどちらも週数間の運用をしました。
その中でSwiftGenの方がいいと思ったので途中で乗り換えました
理由としては

  • UIViewR.stringSwiftGenで管理する理由がそこまでないので、網羅性のあるR.swiftの強みが生きなかった
  • メインで使うlocalizeStringが長いので画面名とか役割とかを付け足すとR.swiftだと長くなってしまう
    • R.swiftだと: R.string.localizable.hoge
    • SwiftGenだと: L10n.hoge
  • 設定する側をR.swiftだとスネークケースで書くとスネークケースのままで生成されてしまうが、SwiftGenだとスネークケースで書いたものをキャメルケースで生成してくれる
  • 末尾に()がつくかどうか
    • コードを書いている時の保管として()なし()(Void)の3つの選択肢が出てくるのでめんどい

の4つが主なものです。

僕は、特に中2つを利用したいがためにSwiftGenを採用しました。特にlocalizeStringの長さの話はすごく重要だと思っています。
良くあるケースとして{ViewControllerの名前}.{UIViewの名前}.{enumの名前}.titleとかを指定した場合は可読性が下がりやすいのでなるべく呼び出すときのデフォルトのクラス名は少ない方が嬉しいです。
また、スネークケース、キャメルケースの話も、長く運用する上では大事だと思っています。Localizable.Stringsでキャメルケースを書くのは自分はあまり好みには思えませんでした。
これらは実際にどっちも運用したことがないと気づけない話なので参考になれば嬉しいです。

一番最初に言った、どちらを導入するか迷っている方でSwiftGenを導入することを大きなコストと捉えていない方はSwiftGenを導入するべきはこれらが理由です。R.swiftは導入しやすい分、で細かいところに届きにくい設計になってしまいます。長期的な運用を考えるとそこのリスクはとるのは難しいかもしれません。

逆に言えば、これらをメリットと感じないのであればR.swiftを使う方がいいと言えます。

どっちを選ぶべきなのか

最終的には当たり前ですが、好きな方を選べばいいという話に落ち着きます。しかし、この記事を見ている方は長期運用を想定した上で文字列管理をしようとしている方が多いのではないでしょうか。めんどくさい作業が発生するので安易に乗り換えることはできないので将来性を加味して選ぶ必要があると思います(経験談)

参考資料

追記

  • structured-swift5, flat-swift5, swift5に対応したので記事を編集しました
  • R.swiftとSwiftGenの比較でのSwiftGenの利点としてスネークケースで書けることを上げていますが、Xcode内での検索の観点からキャメルケースで書くことを個人的に強くお勧めします
49
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?