SwiftUIを使っている方は、誰でも一度はアーキテクチャをどうするかという悩みに直面したことがあるのではないでしょうか。
自分もその一人で、携わっているプロジェクトでUIKitが使われているのを徐々にSwiftUIに移行しようという動きになっているのですが、どのようなアーキテクチャを採用するべきかわかりませんでした。
その際色々なアーキテクチャ、およびSwiftUIとの親和性について調べ、自分なりにまとめたので、今回はそれについて記事にさせていただこうと思います。
アーキテクチャとそれぞれのメリット・デメリット
MV
メリット
- @Enviroment, @FetchedRequestのようなSwiftUI特有の機能を使える。
- 余計なファイルが少なくなる。(例えば、VIPERアーキテクチャは小さなViewであってもpresenter, interactorなどたくさんのファイルを生成することになる。)
- SwiftUIの、データとViewを直接Bindしてレンダリングするという仕様がMVと合致している。
デメリット
- Unitテストが難しくなる。
- 大きなプロジェクトになるほど、どの処理をどこに配置するのかという関心の分離が難しくなる。
- Viewとデータの結合がかなり密になる。
またこれは個人的な意見ですが、Viewが担う範囲が広すぎてしまい、コードが巨大になってしまうのも問題だと思っています。
参考:
https://medium.com/better-programming/mv-state-pattern-a-better-way-of-building-swiftui-apps-2cf2da6652fa
https://www.swiftyjourney.com/swiftui-architecture-a-complete-guide-to-the-mv-pattern-approach
https://azamsharp.com/2023/02/28/building-large-scale-apps-swiftui.html
MVVM
こちらをはじめとして、さまざまな記事でおすすめされています。
メリット
- ビジネスロジックとViewが分離できる。
- テストがしやすい。
-
ObservedObject
を用いることで、ViewとStateをバインドさせてViewを描画するというSwiftUIの特性をうまく活用できる。 - (自分たちのチームでは)すでに使われているアーキテクチャなので、学習コストが低い。
デメリット
- アプリが大きくなると、たくさんのViewModelが出来上がってしまう。
- しかしどのアーキテクチャを利用するにせよ、Viewごとにたくさんの必要なファイルが作られてしまうことは避けられないので、こちらはデメリットでもなさそう。
- ViewModelはViewインスタンスではないため、
@FocusState
,@FetchRequest
などのSwiftUI特有の機能が使えない。
MVSU
こちらで提唱されている方法です。
MVをベースとして、さらにSwiftUIに特化したアーキテクチャに仕上げています。以下簡単な説明です。
- Models: MVVMのモデルと一緒。
@FetchRequest
や@Query
などのSwiftUI特有な機能を利用することができる。 - View: View。全てのstateを保持している。
- Services: ビジネスロジックを記述する層。
- Utilities: 共通で使える処理を記述する層。
メリット
- SwiftUI特有の機能が使える。
- MVVM同様、ビジネスロジックとViewを分離できる。
- DRYの原則に基づき、共通部分を一箇所に集められる。
デメリット
- 実践例が少ないため、Routingをどうするかなど不明瞭な点が多い。
Clean architecture
こちらやこちらなど、実装例は多い。
既存のプロジェクトにクリーンアーキテクチャを導入していくという観点だと、Clean Swiftと呼ばれるライブラリを導入するという手もある。
メリット
- 単一方向の原則に従える。
- ビジネスロジックを分離できる。
- よりコードを疎結合にできる。
- Clean Swift公式ドキュメントが存在するため、そちらを参考にできる。
デメリット
- Viewが増えるごとにたくさんの似たようなファイル、プロトコルが出来上がってしまう。
VIPER
Viewごとにたくさんのファイルが生成されるため、Viewを細かく分割するという観点ではあまり適さないアーキテクチャ。
また、指示的UIに向けて作られたアーキテクチャなので、宣言的UIであるSwiftUIとの相性はあまり良くない。
Redux (TCA)
同じく宣言的UIであるReactでもかなり使われるアーキテクチャ。
http://anuragajwani.medium.com/redux-architecture-adaption-in-swiftui-1b8d36acd177
https://medium.com/neudesic-innovation/managing-swiftui-state-using-redux-525a8879c1be
など、TCAを使わずに実装している例も見受けられる。
メリット
- アプリ全体のstateの把握がしやすい。
- View間のデータのやり取りが必要ない。
- Single source of truthを変更することでアプリ全体のStateを変更するといった特徴が、SwiftUIの値変更時に再描画されるといった特徴と非常に相性がいい。
デメリット
- 学習コストが高い。
- アプリが大きくなった際、stateが多くなりすぎるため、アプリ中全てのstateを把握することが難しい。
またこれも個人的な意見にはなりますが、Reduxをそれ以外のプロジェクトに導入する際にはアプリ全体のアーキテクチャの再編成をしなくてはならないため、既存プロジェクトへの適用という観点ではあまり適さないアーキテクチャなのかな、と思っています。
結論
以上の調査結果から、既存のプロジェクトに導入する際には
- MVVM...SwiftUIの特徴であるStateとViewのバインディングをしつつ、Viewとロジックを分離できる点
新しくプロジェクトを作る際に限って言えば
- Redux...SwiftUIの宣言的フレームワークに合致していて、かつアプリが大きくなっても各層が分離しているため開発しやすさを保てる
が適していると言える。
最後に
今回はこれからSwiftUIを導入し、さらにスケーラブルな構成にしていくためにどのようなアーキテクチャがふさわしいのかを調査しただけになるため、机上の調査結果になっている可能性があります。
間違いやご指摘等、あるいは実際にSwiftUIをプロジェクトで使っていて、こういうアーキテクチャを使っているというのがあれば是非コメントお願いいたします。