Edited at

【iOS】MVVMについて

MVVMについて一からいろいろ調べて自分なりのあるべき姿が

定まってきたので記事としてまとめます。

RxSwiftの要素はできるだけ排除して、純粋にMVVMについての考察をまとめました。

※ただし実装にはRxSwiftの利用を前提としています。


前提

この記事では以下の考え方で話を進めます。


  1. ビジネスロジックは Model で実装する

  2. データバインディングには RxSwift を利用する

  3. 可能な限り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はこんな感じに実装しました。


ViewModel.swift


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日で調べたことを基に考えたことをまとめてみました。

まだ書き足りない所はあるけれど、そこは随時更新していきます。

コードも準備できたらリンク貼りますので、良かったら見てやってください。

まだまだ不十分な部分もあると思いますので、ご指摘いただけますと非常に助かります。

以上です。