iOS 開発における MVVM の Model の実装について
概要
iOS 開発に置いて MVVM が流行りはじめてから随分時間が経ったと思います。
その間にたくさんの方が MVVM や RxSwift に関する知見を公開してくださっていて、自分も参考にさせていただいていました。
しかしその中でも MVVM における Model の実装について色々思うことがあったのでまとめてみます。
Model の実装について思うこと
いろんな記事や OSS の Model の実装を見てみると、結構バラバラなことがわかりました。パターンとしては以下のようになっていました。
1. Model で通信処理等を定義し、結果を Observable で流す
このパターンが一番多いと思います。
Model にあたるクラス等で通信を行い、結果を Observable として ViewModel に公開するパターンです。
Model は通信処理を実装した APIClient や、Repository パターンで作成された Repository に対して依存関係を持っていたりします。
class Model {
func fetchSomeData() -> Observable<SomeData> {
// APIClient のようなクラスで通信処理を実行
return APIClient.call(with: SomeDataRequest())
}
}
2. Model を API のレスポンスとして定義している
xxModel のようなクラス等はなく、代わりに API のレスポンスが定義されているパターンです。
じゃあ通信等はどうするのかと言うと、Service クラスのようなものに対して ViewModel は依存関係を持っていて、それを通して行っているものが多い印象です。( Service クラス等が Model の役割になっているのかもしれません )
class ViewModel {
private let service: SomeService
init(service: SomeService) {
self.service = service
// ここで subscribe したりストリームを合成、変換して View に結果を伝える.
}
}
3. Model が通信処理等の結果を持つ
Model で通信等の処理を行い、結果を BehaviorRelay 等で保持し、ViewModel に公開するパターンです。
MVC の Model に似たような役割をするパターンです。
class Model {
private let someDataRelay = BehaviorRelay<SomeData>
// Observalbe として ViewModel に公開.
var someDataStream: Observable<SomeData> {
return someDataRelay.asObservable()
}
func fetch() {
APIClient.call(with: SomeDataRequest())
.subscribe(onNext: { [weak self] response in
self?.someDataRelay.accept(response)
})
.disposed(by: disposeBag)
}
}
他にも細かい部分が違う物もあるかもしれませんが、大体こんな感じのパターンになると思います。
MVVM 初心者の頃は「MVVM と一括りにしてもこれだけ Model の実装にパターンがあって、どれがいいの?」と思っていました。
そもそも MVVM の Model とは
手元にある iOSアプリ設計パターン入門 には以下のように記されていました。
Model は UI に関係しない純粋なドメインロジックやそのデータを持ちます。MVVM における Model は MVC、MVP における Model と同じ立ち位置です。
MVVM は GUI アーキテクチャに属するため、Model の詳細な責務分けについては関知しません。
なるほど、MVC と同じなら Model はデータを保持し、データの変更等があれば通知するような作りである 3 の Model が正しいのかな?
とはいえ
設計に正解はなく、自分にしっくりくる書き方で作るのが一番だと思います。
今まで 1 と 3 のパターンは実装したことがあるので、それぞれ個人的に感じたメリット、デメリットを挙げてみます。
■ 1 のパターン
Model は結果を流すだけなので、あまり考えずに早く実装することができます。その代わりに ViewModel で Subscribe したりする必要が出てくるので、コード量が多くなりがちです。Model、ViewModel のテストが書けますが、Model のテストはそこまで色んなことは確認できません。
■ 3 のパターン
Model にビジネスロジック、データの保持、通知の役割を持たせるため ViewModel がスリムになり、Model と View 間の橋渡しの役割に集中することができます。その代わりに Model ですべき処理をある程度考える必要が出てくるので、実装スピードは落ちます。
今は 3 のパターンで作るのが一番テストが書けて、ViewModel もスリムになって気に入っています。次は 3 のパターンで簡単なアプリを作って紹介したいと思います。