#記事を移動しました。
https://zenn.dev/dd_sho/articles/ec6545ad371137
今後は上記の記事で更新します。(2021/10/03)
MVVMについて一からいろいろ調べて自分なりのあるべき姿が
定まってきたので記事としてまとめます。
RxSwiftの要素はできるだけ排除して、純粋にMVVMについての考察をまとめました。
※ただし実装にはRxSwiftの利用を前提としています。
前提
この記事では以下の考え方で話を進めます。
- ビジネスロジックは Model で実装する
- データバインディングには RxSwift を利用する
- 可能な限り1クラス1責務にする
1.について
MVVMに関する記事を見ているとビジネスロジックをViewModelで実装するか、Modelで実装するか
の2パターンに分かれています。ViewModelで状態を管理しているからビジネスロジックもViewModel
で実装した方が分かりやすい、という意見はよく分かるのですがViewModelの元々の役割を考えると
Modelで実装した方がいいのではと思いました。
2.について
多くのケースでRxSwiftを採用していることに倣い、RxSwiftを採用します。
RxSwift便利!!
3.について
再利用性を考えると1クラス1責務となるのが理想的だと考えています。
MVVMの構成要素
Model
- ビジネスロジック(通信処理やDB操作など)
- データ型の定義
View
ViewModelのデータを、データバインディングで自動的に描画する
ViewModel
- ViewとModel間の伝達
- Viewのための状態保持
MVVMの処理の流れ
下図のようなイメージです。
上段の左から右への処理は
ViewはViewModelを参照し、ViewModelはModelを参照することで進んでいき
下段の右から左への処理は
ModelからViewModelに通知し、ViewModelからViewに通知することで進みます
(なんでViewModel→Viewの場合だけを指してDataBindingって言んだろ。)
ViewModelの構成要素
ViewModelの役割やどう実装するか非常に悩みましたが、にわかながら現状以下のように自分の中でまとまってます。
皆さんはViewModelをどのように実装されているのでしょうか。
是非コメントお願いします!
検索バーに入力した文字列でGitHubのレポジトリを検索して一覧表示するサンプルを作ってみました。
ViewModelはこんな感じに実装しました。
import Foundation
import RxSwift
import RxCocoa
final class RepositoryViewModel {
struct Input {
// 監視対象(トリガー)
var repositoryName: Observable<String>
}
struct Output {
var rx_repositories: Driver<[RepositoryInfo]>
}
// MARK: - Properties
// Viewから受け取るトリガー
private var _input: RepositoryViewModel.Input!
// Viewにデータをバインドする
private var _output: RepositoryViewModel.Output!
// GitHub APIからデータを取得するModel
private let repositoryModel = RepositoryModel()
// 初期化
init(trigger: RepositoryViewModel.Input) {
_input = trigger
_output = RepositoryViewModel
.Output.init(rx_repositories: createOutput())
}
func output() -> RepositoryViewModel.Output {
return _output
}
}
extension RepositoryViewModel {
// InputからModel経由でOutputを生成する
private func createOutput() -> Driver<[RepositoryInfo]> {
return repositoryModel.rx_get(from: _input.repositoryName)
}
}
プロジェクトにおけるM-V-VMの参照関係
前提の3.「可能な限り1クラス1責務にする」を考えると以下のような関係が良いのではないかと考えます。
View : ViewModel = 1 : 1 (間接的に 1 : n はあり得る)
ViewModel : Model = 1 : 1
Model : Model = n : n
まとめ
ここ2、3日で調べたことを基に考えたことをまとめてみました。
まだ書き足りない所はあるけれど、そこは随時更新していきます。
コードも準備できたらリンク貼りますので、良かったら見てやってください。
まだまだ不十分な部分もあると思いますので、ご指摘いただけますと非常に助かります。
以上です。