ビジネス版マッチングアプリ Yenta iOS版の Build Phase でやっていること

この投稿は、[アトラエ アドベントカレンダー](https://qiita.com/advent-calendar/2021/atrae)11日目の記事です。
昨日は、[@_YukiKawakami](https://twitter.com/_YukiKawakami)の[「ビジネスマッチングアプリ Yenta での MVVM + Flux 例」](https://zenn.dev/ykwkm/articles/11c59d0114f3d8)でした。

---

こんにちは、株式会社アトラエでソフトウェアエンジニアとして働く青野 ([@ysk_aono](https://twitter.com/ysk_aono)) です。

本日は非常にニッチな題材ではありますが、私が開発に携わっている[ビジネス版マッチングアプリ Yenta](https://page.yenta-app.com/jp) iOS版のビルドフェーズで実行していることについて書いてみようと思います。

※はじめに断っておきますが、特別なことは何もしていません。笑

## 全体の概要

スクショで見てみるとこんな感じです。
Swiftファイルのコンパイルや、CocoaPodsが自動で設定してくれるスクリプトが入っていますね。

![Screen Shot 2021-12-11 at 0.48.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1795/d2fa392b-d5a0-b0b5-ab3d-e348e7ca7b55.png)

## 各フェーズについて

この中の

* SwiftLint Run Script
* SwiftGen Run Script
* Copy Localizable files, Copy files generated by Swiftgen
* Copy Google Service Info Plist
* LicensePlist Run Script

について少しずつ触れます。

### SwiftLint Run Script

開発時に継続的にコードを静的解析するため、ビルドフェーズの中で[SwiftLint](https://github.com/realm/SwiftLint)をローカル実行しています。コードの整形 & 自動修正も同時に行なっています。

スクリプトの中身は以下です。

```sh
if [ $CONFIGURATION = "Debug" ]; then
  git diff --name-only | grep ".swift$" | while read filename; do
    ${PODS_ROOT}/SwiftLint/swiftlint --fix --format --path "$filename"
    ${PODS_ROOT}/SwiftLint/swiftlint --path "$filename"
  done
fi
```

もともとは毎回全`.swift`ファイルを走査していましたが、先日SwiftLintによるwarningを全て潰し終わりました。そのため、現在は`git diff`で出力された.swiftファイルのみをLintするように変更しています。

少し話が逸れますが、この変更によってコードのビルド速度に向上が見られました。当然の結果ではあるのですが、開発者体験が大きく改善しています。

変更前↓
![unnamed-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1795/519f7fa7-0adb-e34a-63f8-0231b7dea4fe.png)

変更後↓
![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1795/4dfe9325-4281-fd86-b84d-4af09fd8df3c.png)


### SwiftGen Run Script

[SwiftGen](https://github.com/SwiftGen/SwiftGen)を使って、画像や翻訳用辞書といったリソースをSwiftファイルとして自動生成し、タイプセーフに利用できるようにしています。画像やローカライゼーションのキー名の文字列をハードコードするのは嫌ですからね...

```yaml:swiftgen.yml
strings:
  inputs: Localization/ja.lproj
  filter: .+\.strings$
  outputs:
    - templateName: structured-swift5
      output: ./Yenta/Generated/Strings.swift

xcassets:
  inputs:
    - Images.xcassets
    - ColorPallete.xcassets
  outputs:
    templateName: swift5
    output: ./Yenta/Generated/Assets.swift
```

上記のような設定ファイルを使い、SwiftGenを実行しています。

```sh
${PODS_ROOT}/SwiftGen/bin/swiftgen
```

### Copy Localizable files, Copy files generated by Swiftgen

現在Embedded Frameworkを利用してコード群をいくつかのレイヤーに分けており、依存方向の制御や責務の分離、ビルド速度の向上を図っています。

この構造の影響で、フレームワークを跨ぐと呼び出せないファイルも存在します。SwiftGenの自動生成対象にしている翻訳ファイルもその1つとなってしまっており、これに関しては例外的にどのフレームワークからも利用できるようにしたいというニーズがあります。そのため、翻訳辞書本体と呼び出し用のSwiftファイルを各フレームワークにコピーしています。

以下のようなイメージです。

辞書の本体↓

```sh
cp -r ${PROJECT_DIR}/Yenta/Localization/* ${PROJECT_DIR}/YentaDomain/Localization-copy/
```

SwiftGenによって生成された`Strings.swift`↓

```sh
cp -r ${PROJECT_DIR}/Yenta/Generated/Strings.swift ${PROJECT_DIR}/YentaDomain/Generated-copy/
```

### Copy Google Service Info Plist

アプリ開発には欠かせない存在となっているFirebaseのサービス群。これらの導入時に必要なのが、Firebase Apple プラットフォーム構成ファイル（`GoogleService-Info.plist`）です。

アプリのBuild ConfigurationやSchemeが1つしかない状態で運用している場合は特に考慮する必要はない事項ですが、開発環境/本番環境への接続を切り分けたりしたいので複数で運用しているケースも多いのではないでしょうか。その際、各設定毎に異なる`GoogleService-Info.plist`が欲しくなります。

そこで、以下のように異なるplistファイルを1つだけ配置するようにしています。

```sh
if [ "${CONFIGURATION}" == "Release" ]; then
cp "${PROJECT_DIR}/${PROJECT_NAME}/GoogleService/Release-GoogleService-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
elif [ "${CONFIGURATION}" == "AdHoc" ]; then

...

fi
```

### LicensePlist Run Script

[LicensePlist](https://github.com/mono0926/LicensePlist)を使って、3rdパーティライブラリのライセンス情報一覧を生成しています。非常に便利で助かっています

```sh
if [ $CONFIGURATION = "Debug" ]; then
${PODS_ROOT}/LicensePlist/license-plist --output-path $PRODUCT_NAME/Settings.bundle
fi
```

## おわりに

淡々と書いてきましたが、やはり特段変わったことはしていないですねw

最近になって[unused.rb](https://github.com/PaulTaykalo/swift-scripts/blob/master/unused.rb)の存在を知り、これもビルド (もしくはGitHub Actions) 時に実行したら便利かな？と考えていたりします。ビルドは開発時に必ず実行するアクションではあるので、何らかの自動化スクリプトを挿入する余地が多分にあり地味に面白いなと書いていて感じました。

各アプリがこのBuild Phaseでどういうことをやっているのか、Google検索しても案外出てこなさそうな領域な気もするので、他の事例を知ってみたいなとも思います。

短いですが、今回は以上です!!

---

明日は、インフラエンジニアの[@shino59](https://qiita.com/shino59)が記事を書く予定です。お楽しみに！