iOS開発のコードフォーマット運用例
こんにちは!株式会社PREVENTでiOSアプリエンジニアの佐藤です。
この記事はPREVENTアドベントカレンダーの4日目の記事です。
みなさんiOS開発において、コードフォーマットは行っているでしょうか。
今回は弊社のiOSプロジェクトでどのようにコードフォーマットを行っているのか、運用の話をメインに紹介します!
コードフォーマットとは
コードフォーマットとは、特定のルールに従って、コードを一貫したスタイルに整形することを指します。
現在のプロジェクトでは、以下を期待してコードフォーマットを導入しています。
- コード全体を一貫したスタイルに統一することで、可読性の向上を図る
- 開発者間のコーディングスタイルのクセをできるだけ排除し、コードの品質を一定に保つことで保守性の向上を図る(可読性の向上にもつながりますね)
- コードレビュー時にコーディングスタイル関連の細かい指摘を減らして、より本質的なレビューに専念できるようにする
現在のプロジェクトのコードフォーマット運用
では、現在のプロジェクトでは具体的にどのような運用を行なっているのかを、実装フローベースで紹介します。
何かコードの修正を行おうとした時以下のような順番で、実装を進めると思います。(プロジェクトによって多少違いはあるでしょうが、だいたいこんなものと思っています)
- 作業ブランチを作成
- 作業ブランチで、コードを修正する
- 修正内容をコミット
- 修正が完了するまで2, 3を繰り返す
- ブランチをリポジトリにプッシュしてPRを作成
- PRレビュー
- マージ
上記フローの中で、3のコミット時にコードフォーマットを行う運用にしています。
Gitにはpre-commitフックというコミットの前に任意のスクリプトを実行するインタフェースが提供されており、これを利用してフォーマッターをCLIでキックするという流れです。
このように、コミット時に自動でコードフォーマットを行うことで、リポジトリで管理するコードが漏れなくフォーマットを行うことができます。(gitの操作によっては、pre-commitフックをバイパスすることもできるので、そこは運用ルールでカバーする必要はあります)
また、開発者の操作に依存せず実装フローに従って開発すればコードフォーマットが漏れることがないので、開発者の負担がかなり低いのが特徴です。
この運用を始めた意図としても、コードフォーマット漏れを防ぎかつ開発者の負担が少ないようにしたかったところがあったため、意図とマッチした運用になっていると思っています。
pre-commitフックを使ったコードフォーマットスクリプトの実装は、以下記事が大変参考になりました。
【その他の運用例】ビルド時にコードフォーマットを行う
タイトルの通り、ビルド時にコードフォーマットを行います。
ビルドフェーズのRun Script、BuildToolPluginなどで、フォーマッターを実行する処理を追加することで、ビルド時にコードフォーマットを行うことができます。
実は現在のプロジェクトでも以前はこの運用になっていましたが、以下課題があったため、先に紹介した運用に移行しています。
コードフォーマットが漏れるケースがある
基本的にリポジトリにプッシュする前にビルドすると思いますが、急いでいる時やビルドの後にちょっと手直ししてからその後ビルドせずにコミットしてプッシュしたりと、地味にビルドせずにリポジトリにプッシュされてしまうケースが多く、気づいたらコードフォーマットがかかっていないコードだらけになっているということがありました。
管理しているコード全てにフォーマッターが適用されていないと、コードの品質を一定に保って保守性や可読性を担保するといったコードフォーマットのメリットを享受できないので、コードフォーマットを行う意味が薄れてしまうという事例です。
Previewを使ったUIの実装との相性が悪い
SwiftUIの登場からPreviewでどのようにUIが表示できるかどうかのフィードバックを受けながらUIを実装するスタイルが徐々に定着してきたかと思いますが、ビルド時にコードフォーマットを行う運用と非常に相性が悪かったです。
具体的な困りごととして、Previewを起動しながら対象のUIのコード修正を行うと、結構な頻度でフォーマッターによるコードの編集と開発者のエディタの入力によるコードの編集が競合してしまい、その旨のアラートが表示されてしまうということです。
これ地味に開発体験がかなり悪いです。
なぜこのようなことが起こるかというと、Preview起動中にコードの修正が入ると都度自動ビルドが走りフォーマッターがキックされるため、そのタイミングでエディタからもコードの編集をしていると競合が発生することになるのです。
ただ、それだとRun Scriptなどで「Previewによるビルドであればコードフォーマットしない」というふうに判定すればいいだけでは、と感じるかもしれません。
ただ、Xcode16から導入された新しいPreviewエンジンを使っている場合、ビルドフェーズでPreviewによるビルドかどうかの判定ができなくなっています。
※Xcode上タブの"Editor->Canvas->Use Legacy Previews Execution"にチェックがついていなければ、新しいPreviewエンジンを使っていることがわかります。

これはAppleのフォーラムのスレッドでも議論されていましたが、結論としてはPreviewと通常のビルドはビルドプロセスもキャッシュも全く同じものを使っていて区別する必要がないため、今後も新しいPreviewでビルド時にコードフォーマットを行うのは難しいと思われます。
また、同スレッドではそういったユースケースであれば、ビルド時ではなくコミット前に実行することを推奨されていました。
まとめ
今回は現在のプロジェクトのコードフォーマット運用を紹介しました。
この運用を適用すれば、開発者が意識せず、もれなく管理コードのスタイルを統一できます。
SwiftのフォーマッターはSwiftFormatやSwiftLint、swift-formatなど複数ありますが、いずれもCLIツールとしても使えるので、本運用にはいずれも適用できるかと思います。
導入も比較的簡単だと思うので、これからコードフォーマットを導入したいだったり、現状の運用に課題がある場合に参考になれば幸いです。
Discussion