SwiftUIをClean Architectureで実装してみました
株式会社バニッシュ・スタンダードでiOS向けアプリの開発をやっている広瀬です。
弊社では順次SwiftUIに移行中なんですが、SwiftUIを利用するにあたってアーキテクチャに何を採用するのかは議題にあがりがちだと思います。
多くはMVVMを使っていることだと思いますが、弊社では一部のiOS向けアプリでClean Architectureを採用して開発を進めています。
今回はなぜClean Architectureを採用するに至ったのか、またどの様に利用しているかを話していこうと思います。
解決したい問題
なぜClean Architectureを採用するに至ったかですが、複数画面に渡って値の受け渡しをしたい場合にViewModelを経由しての受け渡しが難しくなるケースがあったためなのと、SwiftUIのPreviewを利用する場合にロジックとpropertyを切り離したほうが容易なのではと考えたためでもあります。
ではさっそくサンプルをみていきます。(適当に作っているので実行はできません)
クラス構成
クラス構成は以上の様にClean Architecture +αの形になっています。
(UseCase以下は略)
以下、presentation層の実装を紹介します。
実装
struct TopView: View {
@ObservedObject private var state: TopViewState
private var action: TopViewAction
init(state: TopViewState, action: TopViewAction) {
self.state = state
self.action = action
}
var body: some View {
VStack {
if state.isLogin {
HomeView()
} else {
LoginView()
}
}
.onApear {
action.onApear()
}
}
}
#Preview {
TopView(state: TopViewStateMock(), action: TopViewActionMock())
}
final class TopViewState: ObservableObject {
@Published var isLogin = false
@Published var showError = false
init() {}
}
struct TopViewAction {
let delegate: TopDelegateAction
init(state: TopViewState) {
self.delegate = TopPresenter(useCase: LoginUseCase(), state: state)
}
func onApear() {
delegate.onApear()
}
}
protocol TopDelegateAction {
func onApear()
}
final class TopPresenter {
private let useCase: LoginUseCase
init(useCase: LoginUseCase, state: TopViewState) {
self.useCase = useCase
self.state = state
}
}
extension TopPresenter: TopDelegateAction {
func onApear() {
Task {
do {
state.isLogin = try await useCase.login()
} catch {
state.showError = true
}
}
}
}
ご覧の様にMVVMと比較した際にViewModelが担当していたoutputはViewStateに、inputがViewAction、ロジックはPresenterが受け持っています。
おわりに
今回紹介しましたアーキテクチャを採用することで、Previewを簡潔に書ける様になったのと、ViewModelの責務を分割したことで、Previewする際に肥大化するViewModelを参照する必要がなくなったためビルドプロセスを軽くすることもできました。
また、ViewStateをStoreに変更するなどした場合など、グローバルにやりとりをすることもやりやすくなりました。
まだまだこのアーキテクチャは完璧ではなくて色んなケースで問題が発生していて試行錯誤の段階ですが、以上、弊社iOSアプリで採用しているアーキテクチャの簡単な紹介でした。
次回移行アップデートあればまた続きを執筆したいと思います。
Discussion