大規模なiOSアプリ開発を2年以上継続して得られた知見

# 前書き

それなりに大規模なアプリの新規開発〜機能追加を行う中で得られた知見をまとめます。

アプリのジャンル／ライフサイクル、チームメンバーのスキルレベルなどによって適用できない事項もあるかと思いますが、一つの事例として参考にしていただければ幸いです。

＜アプリ／チームの概要＞

- iOS版開発チームとしては最小4人〜最大9人。
    - その他、Androidチーム、バックエンドチーム、デザインチーム…などなど諸々合わせて総勢20人〜30数人
- 画面数は約120。
- アプリの累計ダウンロード数は数百万。
- App Store上のカテゴリは「ファイナンス」。
- リリースサイクル（計画リリース）は平均で2ヶ月に1回。
- 開発環境はXcode + Swift。
- Scrumで開発。

本文・コメント含め、これ以上に具体的なことは書けませんのでご了承ください。
本記事の内容はあくまで私個人の見解であり、所属企業における立場、戦略、意見を代表するものではありません。

# UI構築

## ポリシー

- 一時期はStoryboardと、コードベース・レイアウトが混在していた時期もありましたが、現在ではコードベース・レイアウトのみに統一しました。
- 理由は以下のようなStoryboardのデメリットを感じることが多くなってきたためです。
    - 人数が多いとStoryboardのconflictが多く、その解消がとにかく面倒臭い。
    - Storyboardでは、画面修正のレビューが辛い。
    - アプリ規模が大きくなるにつれ、Xcode上でStoryboardを開く時の待ち時間が長くなってしまった。
    - デザイナーからのUI/UX要求レベルが上がってきて、画面の状態が複雑になってきた。
        - 例えば、ローディングプレースホルダを表示しつつ裏でAPI通信＞取得成功時は通常View、エラー時は特殊なView、など。。。
        - Storyboardでは静的な状態はプレビューできるが、上のような動的な状態変化が多いと意味がない。
    - Storyboardでは、フォント、色など、アプリの統一ポリシーを適用し忘れることがしばしばある。
- ただし、Unwind Segueは便利なので、画面遷移はStoryboardを使っています。
- コードベース・レイアウトを容易にするために、自作ライブラリを構築しました（productionコードなので公開はできません）。

参考リンク：
[iOSアプリケーションでコードベースのレイアウトを積極利用する](https://techlife.cookpad.com/entry/2015/11/04/180000)
[[Xcode] [Swift] Unwind Segueで2つ以上前の画面に一気に戻って画面を再更新する](https://qiita.com/y-some/items/c0110253650ec1f7da14)

## 評価

- コードベース・レイアウト統一に至るまでに紆余曲折はありましたが、私たちのアプリではコードベース・レイアウトが生産性・品質・保守性ともにベストな方法である、と評価しています。
- ただし、画面遷移のみStoryboardに依存している状態はあまり「見通し」がよくないので、改善の余地がありそうです。

# アーキテクチャー・パターン

## ポリシー

- 当初は標準的なCocoa MVCで作っていました。当然FatViewController化しました。
- アプリの規模が大きくなり、またエンハンスメントを重ねるごとに改修が困難になってきました。
- このため、MVVMへのリアーキテクチャーを進めました。
    - 一気に行わず、新規機能や大規模改修に合わせて実施。
    - ライブラリは使わず、設計パターンだけを適用。
- MVVMアーキテクチャーを選定した理由：
    - Clean Architectureなどの複雑なアーキテクチャーよりも、MVVMへの変更の方が改修規模が小さかったため。
    - メンバーのスキルレベルに差があり、MVVMより複雑なアーキテクチャーを全員が習熟することは困難であったため。

興味のある方は、以下の記事をご参照ください。
[大規模なiOSアプリでFatViewControllerを解消するために導入したMVVMパターンのサンプル](https://qiita.com/y-some/items/a6b8b75747f4a5d48ec9)

## 評価

- 保守性・拡張性が上がったと評価しており、今後も継続する予定です。

# ライブラリ管理

## ポリシー

- ライブラリに起因するトラブルは結構多いので、ライブラリ導入は必要最小限に止めるようにしています。
    - 一例：[iOS14でのコピペ内容(UIPasteboard)取得警告について ※Firebase DynamicLinksを導入している場合は要注意！](https://qiita.com/y-some/items/485d5d09f37ccb8fffd8)
- ライブラリ管理ツールとして[Carthage](https://github.com/Carthage/Carthage)を使っています。
    - [CocoaPods](https://cocoapods.org/)よりもビルドが早いのでメリットを感じて導入しました。

## 評価

- ライブラリに起因するトラブルは開発計画を大きく狂わせるので、今後も必要最小限に止める方針に変わりないと思います。
- Carthageについては、2020年9月現在で[Xcode 12でビルドエラーになるというクリティカルな問題が発生している](https://github.com/Carthage/Carthage/issues/3019)ので、CocoaPodsに近日中に乗り換えるかもしれません。。。
- 大規模アプリにおいては、ライブラリ管理ツール、導入ライブラリ共に、その選定は慎重にならざるを得ないです。

# コードレビュー

## ポリシー

- IBM Cloud上に[GitLab](https://ja.wikipedia.org/wiki/GitLab)を導入しました。
- コードレビューは99.9%実施しています。
- GitLabのマージリクエスト上でレビューコメントをやりとりするスタイルです。
- レビューアーは原則として複数人としています。
    - 一次レビューはメンバー
    - 最終レビューはリーダー
- コードレビューの目的は以下の通りと宣言しており、その観点で気になったことがあればレビューコメントを書くようにしています。
    - システムのメンテナンス性、リーダビリティ、理解のしやすさを改善すること。
    - ブラックボックステストでは検出しづらい不具合（例：メモリーリーク）を発見すること。
    - 知識を共有すること。
- 一応、コーディングガイドラインはありますが項目は最低限です。
    - 「強制アンラップは理由がない限りは使わないようにしよう」「インデントは合わせよう」「Xcodeの設定を合わせよう」程度です。
    - 私見ですが、ガチガチに縛ろうとして規約を増やしたところで、読むことが大変になるだけで、完璧に守ることは困難です。
    - 規約で縛るよりも、上のような「目的」を共有することの方が大事では、と考えます。

参考リンク：
[[Xcode] 実務的Tips: チーム内でXcodeの設定項目を合わせて不要な修正差分が出ないようにする](https://qiita.com/y-some/items/81ef7bfeb4ae9990b86b)

## 評価

- 品質・保守性には大きな効果が認められ、コードレビューを行わないという選択肢は今後もありません。
- 以下の点は要改善ポイントです。
    - リーダーの負荷が高いこと（レビューに追われて自分の担当分の実装時間が削られる）。
    - リーダーがボトルネックになること。
        - リーダーが不在だとマージできない。
        - 先行タスクのマージリクエストがレビュー未完了だと後続作業が滞る。

# テスト

## ポリシー

- １年ぐらい、XCUITestによるUIテストの自動化に取り組みました。→詳細は「参考リンク」の記事を参照
- しかしながら、機能改修時のメンテナンスコストがあまりにも大きかったです。
    - コード修正は1時間でも、既存のテストの修正に半日以上かかることもザラ。。。
- このため、現在では以下のような手法を取っています。
    - ロジック部分のテストはXCTest。
    - UI部分のテストは実動作で確認。
    - APIについては、Node.jsベースのスタブツール（ローカルサーバー）を自作し、正常系/異常系レスポンスのテストを行う。
- 以上は開発チームとしてのテストで、業務観点での総合テストは別途QAチームがテストシナリオに即して行う。

参考リンク：
[XcodeのUIテストフレームワーク「XCUITest」のTips](https://qiita.com/y-some/items/d0c32f6e60e8ea2367fa)
    
## 評価

- 私たちのアプリではUIの改変が多いため、XCUITestは費用対効果の観点で合いませんでした。
- MVVMパターンへのリアーキテクチャーとの「合わせ技」で、なるべくView/ViewControllerには画面表示以外のロジックは書かないようにし、ViewModelのXCTestを増やすように取り組んでいるところです。

# CI/CD

## ポリシー

- 以下の目的で、CI/CDを取り入れています。
    - Sprintの途中であっても、マージされた機能はいち早くQAエンジニアに渡してテストしてもらいたい。
    - あるいは、オーナーやデザイナーに動かしてもらい、フィードバックしてもらいたい。
- 現状は、[Jenkins](https://www.jenkins.io/)でGitLabのブランチを監視して、[fastlane](https://fastlane.tools/)をキックしてビルドを走らせ、TestFlightにアップロードしています。
- テストは自動で走らせるものの、失敗はSlackに自動postするだけで、デプロイは止めません。
    - 緊急のバグ修正時などはテストコードの修正は後回しにするケースもあるので…

## 評価

- アプリの規模が大きくなるに応じてビルド時間が長くなるので、大規模アプリでは自動デプロイは必須です。

# 後書き

- OSも開発環境も絶えず進化している中で、大規模なアプリを、迅速かつトラブルなくリリースし続けることは容易ではない、と感じます。
- 「早いサイクルで小さい改善を積み重ねる」をモットーに、まだまだ模索を続けて行こうと考えています。