はじめに
CleanArchitectureのアプリをSwiftUIに落とし込む場合、どのようなアーキテクチャが良いのかを考えてみました。
現在、SwiftUIでは色々なアーキテクチャの形が模索されている状況かと思います。
その中の一つのアイディアとして皆様の参考になる部分があると良いなと思い、案を共有させていただければと思います。
アーキテクチャ図
ViewController
- MainViewとPresenterを持つ
- MainViewをHostingControllerでViewController化し、addChildする
- ViewとPresenterのハブとしての役割
- Viewから来たアクションなどのイベントをPresenterに通知
- Viewを生成する際に、PresenterからViewModelをViewに渡す
- ライフサイクル系のイベントはUIViewControllerで管理する
- SwiftUIではUIKit起因のライフサイクルを全てカバーできないため
- 画面遷移も担当する
- 画面遷移まで担当することで、SwiftUIの影響を本画面内に収めることができる
- 別画面は本画面がSwiftUIを使っていることを意識してなくて済む
SwiftUI.View
MainView
- UIViewControllerに渡すView
- 複数のViewからなる画面の場合は、最終的には全てこのView内に収まるようにする
- UIViewControllerからViewModelを受け取り、各SubViewに渡す
- 各SubViewからユーザーイベントなどを受け取り、UIViewControllerに伝える
※ 再利用性がないパーツで構成されている場合はMainViewのみでSubViewを作成する必要はない
SubView
- 画面内に存在する各要素、パーツを一つのViewとして定義する
- 一つのSubViewに対して一つのViewModelが存在するようにする
- ユーザーによるアクションが発生した場合はMainViewに通知する
Presenter
- 一画面(UIViewController)につき一つのPresenterを保持
- ViewModelを保持する
- Controllerからイベントを受け取って処理を実施し、実施後に必要に応じてViewModelを更新する
ViewModel
- ObeervableObject
- SubViewと対になる形で存在
メリット
- UIViewControllerをparentにすることでUIKitのライフサイクルが使える
- 他画面からはUIViewControllerとして見られるので、SwiftUIの影響範囲が本画面内のみに限られる
- 上記から、StoryBoardとSwiftUIの共存が容易になります。
- SubViewとViewModelを一対にすることで、各パーツを別画面で使う場合の再利用が簡単になる
- PresenterをObeervableObjectにしないのは上記理由
- Preseterは画面に紐づくので、別画面でもSubViewを使用したい時に、同様の処理を別のPresenterでも書く必要が出てくる
デメリット
- ViewModelの受け渡しが煩雑
- Presetner→ViewController→MainView→SubView
- UIViewControllerとMainViewはそれぞれがハブとしての役割が強いため、PresenterからSubViewに行くまでにハブを2回通していることになる
- StoryboardとSwiftUIが共存せず、SwiftUI単体の場合は、UIViewControllerにSwiftUIを持たせるのは冗長な構成になってしまっていると思われる
おわりに
まだまだ、考慮が足りていない部分などが多く存在すると思います。
もし、改善案やご意見等ありましたらコメントいただけますと幸いです。