Kyash iOSリファクタリング裏話 - Assets編

こんにちは。iOSエンジニアのJumpeiです。前回のリファクタリングの記事では主にアーキテクチャの見直しについて触れましたが、今回はStoryboardやAssets、ローカライズ周りで整理したことを紹介したいと思います。

未使用のAssetsを削除

開発する上で既存の画像を置き換えたり、新たに追加することはよくありますが、その時に古い画像を消し忘れることはないでしょうか。ファイル名が同じであれば置き換えるだけで古いほうは削除できますが、そうでない場合、こまめに削除しないとどんどん未使用なファイルが溜まってきます。リファクタ前のKyashアプリは不必要なAssetsが多くあったのですが、Assets.xcassets内の画像数が多すぎて(リファクタ時の弊社アプリは180ぐらいありました)ぱっと見、どれが未使用なのか把握することが困難でした。FengNiaoを使えば未使用なAssetファイルを一気に削除できるのですが、その当時は存在を知らなかったため、愚直に探して削除を続けました。

github.com

今後の対策としてまず、Assets.xcassetsをトピック別に分割することにしました。ウォレット画面で使う画像はWallet.xcassets、アカウント画面のものはAccount.xcassetsのように小分けすることで各ViewControllerで使う画像が分かりやすくなりました。

また新しい画像の追加時に必ずImageOptimを当てることにし、できるだけファイルサイズを減らすことにしました。

ImageOptim — better Save for Web

実際にどれぐらい圧縮できるかですが、下のスクリーンショットがその結果です。 f:id:jumpei-katayama:20180402111418p:plain

この例だと半分以下にまで圧縮できました!

使い方も簡単で、ImageOptimを開いた時に表示されるWindowに圧縮したい画像が含まれるフォルダをドラッグアンドドロップするだけでいいので是非使ってみてください。

カラーパレットをバージョン管理

普段の開発ではZeplinのデザインを参考にUIを作っています。色の指定もhexコードをそのままStorybaord上やコードで設定しています。しかし、Storybordの場合カラーパレットをGenericRGBからDeviceRGBに変更しないと、Zeplink上のカラーと色が若干違って見える問題もあり、数値のみの共有には課題がありました。そこでカラーパレットをバージョン管理に追加し、メンバー間で常に同じ色を使えるようにしました。

f:id:jumpei-katayama:20180330142915p:plain

後ほど説明しますが、このカスタムカラーパレットはSwiftGenで使うことも可能です。

つまり、.clrファイルが一つあるだけでStoryboardとコードで常に同じ色を使えることが保証されます。

LocalizableStringをもっとシンプルに

KyashのiOSアプリは多言語対応を考慮し、Localizable.strings内に文言をすべてまとめ、テキストの表示はViewController内で行っていました。以下が実際のコードの一部です。

NSLocalizedString("ALERT_USER_ABOUT_TAKING_SCREENSHOT_OF_VIRTUAL_CARD",
 comment: "Remember to not share your screenshots of your virtual card number.")

しかし、これにはいくつか問題があります。

まずキー名やコメントが長いとコード量が増えてしまいます。しかも文言に変更が合った場合、Localizable.stringのキーと値だけでなく、それが使われている箇所両方変えないといけないため手間が増えます。もしその文字列が複数のviewControllerで使われている場合、意図しない画面の文言も変わってしまうので注意する必要がありました。

そこで、キー名は<画面名>.hogeのように命名し、重複利用を防ぐようにしました。これに加え、後ほど説明するSwiftGenを使うことでメンテしやすいコードになりました。

Storyboardのコンフリクトを防ぐ

KyashのiOSアプリはUIのほとんどをStoryboardで作っています。Storyboardは非常に便利なのですが、一つのStoryboardが複数シーンをもつとコンフリクトが起きやすいデメリットがあります。リファクタ前には、あるStoryboard内のrootViewControllerから大量にsegueが出てきて、かつStoryboard referenceを使っていなかったりもあり、小さな改修も苦労しました。(Segueを使うべきかどうかは物議を醸しそうですが、弊社では徐々になくなってきてます。)

これを解決するために、一つのStoryboardに一つのシーン(ViewContoller)を表示することにしました。これにより複数人が同じStoryboardを編集することが起きにくくなりコンフリクトはほぼ起きなくなりました。

SwiftGenの導入

github.com

SwiftGenは今回のAssets関連のリファクタで最も役にたったツールです。他にR.swiftも検討しましたが、よりコミュニティが活発との理由でSwiftGenを選びました。 ハードコーディングでUIImageやNSLocalizedStringのインスタンスを作る必要がなくなるので、typoによるランタイムのクラッシュを防止げ、変更があった場合もすぐに対応できます。

NSLocalizedStringに関してはコード補完時に実際のテキストが表示されるので、わざわざもとのファイルを確認する必要がなくなり、実装のスピードも上がります。

f:id:jumpei-katayama:20180402111412p:plain

またビルド時にファイルを生成してくれるので、extensionを使ったショートカットを自分で定義する必要もなくなります。 ビルド時にswiftgenを実行するため、CIに影響があるのではと思うかもしれませんが、弊社が利用しているBitriseだと約3秒なので大きな影響はないと考えています。

以下がSwiftGenを使ってAssets周りを表現した結果です。

Assets

Before

imageView.image = UIImage(named:"walletLogo")

After

imageView.image = Asset.Wallet.logo.image

LocalizableStrings

Before

let title = NSLocalizedString("ALERT_USER_ABOUT_TAKING_SCREENSHOT_OF_VIRTUAL_CARD", comment: "Remember to not share your screenshots of your virtual card number.")

After

let title = L10n.Wallet.screenShotAlert

Color

Before

view.backgroundColor = UIColor.blue

After

view.backgroundColor = ColorName.blue.color

まとめ

今回やったことをまとめると

  • 1 Storyboard 1ViewController
  • アプリ内で使う色はカラーファイルをgitで管理しXcodeのカラーパレットから使う
  • Assetはトピックごとに分け、視覚的に未使用なものが分かりやすくする
  • SwiftGenを使ってLocalizableStirng、Asset、colorを管理する

です。これらのリファクタは個別に見れば難しいことはありません。もちろんアプリの規模や既存のコードの状態によって適用するのが難しいこともあるかもしれませんが、それぞれひとつずつやってみることもできるのでぜひ検討してみてください。