【Swift】MVVMアーキテクチャとは?ViewModelの役割

この記事からわかること

  • MVVMとは?
  • Model View ViewModel役割
  • 特徴メリット
  • 設計方法実装例

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

アプリ開発を行う上で重要になってくる設計思想(アーキテクチャ)の1つである「MVVM」についてまとめていきます。

おすすめ記事:【GoF】23種類のデザインパターンとは?Swiftでよく使う活用例

おすすめ記事:SOLID原則とは?Swift(iOS)で理解する設計開発思想

MVVMとは?

MVVMとは「Model View ViewModel」の略称でアプリなどのソフトウェア開発に適応される設計思想(アーキテクチャ)の1つです。アプリの開発は1人で行う場合もあればチームで開発する場合もあります。どちらにおいても似たようなコードが複数記述されていたり、オブジェクト同士の依存が強い場合など、コード自体の可読性や拡張性が低いと改修コストが大幅に増えてしまいます。アーキテクチャに沿った設計をすることで、拡張性や保守性、作業性、再利用のしやすさなどが向上するというメリットが受けられます。

さらにその中でもMVVMとはアプリケーションなどといった画面への描画処理を持ったソフトウェアにおけるGUI(Graphical User Interface)アーキテクチャの1つです。他にもMVCやMVPなど種類がありますが、MVVMはプログラムを3つの責務に分かれた構造で設計する考え方です。それぞれの責務は大まかにみると以下のように分かれます。

MVVM の3つの要素

そしてそれぞれの頭文字「Model View ViewModel」をとってMVVM(アーキテクチャ)と名付けられています。

ちなみに別の設計思想であるMVCもiOSアプリ開発ではよく用いられます。公式によるとUIKitフレームワーク自体がMVCアーキテクチャーに準じた構造で設計されているようです。またMVCは比較的取っ付きやすい構造なので初心者はこちらから学習することをおすすめします。

おすすめ記事:Swift(UIKit)のMVCアーキテクチャーとは?役割と構造まとめ

iOSアプリ開発とアーキテクチャ

Swiftを使用したiOSアプリ開発ではMVVMに倣って開発されることが多いです。Swift UIが発表されてからはMVVMとの親和性は少し下がっているようですが、UIKitベース(Storyboard)で開発する際にはMVVMがよく使われている印象です。

アーキテクチャに準じない設計で開発していく場合、UIKitベースでは画面を管理するViewControllerクラス内のコードが肥大化しやすく、流れも掴みづらい構造になりがちです。ViewControllerクラスというどっちつかずの命名も責務の境界をぼんやりささている原因の1つかもしれません。

MVVMに限らずアーキテクチャに倣った開発を行うことでコードの目的と役割が明確になり、予期せぬバグを抑止できたり保守性の高いアプリ開発が可能になります。

またRxSwiftと呼ばれるライブラリ(後述しています)がMVVMとセットで使用されることが多いです。

MVVMを構成する3つの要素

MVVMは「Model・View・ViewModel」の3つの要素に分かれていました。これらの役割や持たせるべき処理などを具体例を見ながらもう少し深掘りしていきます。

また実装例としてクラスプラットフォームデータベースライブラリである「Realm」を使用したコードも載せておきます。

おすすめ記事:【SwiftUI】Realm Swiftとは?導入方法とCRUD処理のやり方

Model

Model(モデル) とはデータ部分を司っている要素です。データベースに格納されている情報通信処理などがまさしくモデルで扱う部分であり、データにおけるCRUD(クラッド:Create, Read, Update, Delete)などの処理も実装します。

例えば取得したデータを表示するために整形するのもこの要素の努めになります。日付のフォーマットや数字の表し方など、他の部分にはデータに関するロジックは含ませないようにしてモデル内でデータに関するロジックを終了させます。モデルから受け取ることができる状態のデータがそのまま使われるようにすることを意識して作成するのが大事になるのだと思います。

具体例:ネットワークAPI、ローカルデータベースなど


import UIKit
import RealmSwift

class RealmDataBaseModel {

    private let realm = try! Realm()

    // MARK: - Create
    public func createRecord(notice:Notification){
        try! realm.write {
            realm.add(notice)
        }
    }
    
    // MARK: - Read
    public func readIdRecord(id:UUID) -> Notification{
        return realm.objects(Notification.self).where({$0.id == id}).first!
    }
    
    // MARK: - Update
    public func updateRecord(noticeRecord:Notification,body:String,date:Date){
        try! realm.write {
            noticeRecord.body = body
            noticeRecord.date = date
        }
    }
    
    // MARK: - Delete
    public func deleteAllOldRecord(){
        try! realm.write{
            let result = realm.objects(Notification.self).where({$0.date < Date()})
            realm.delete(result)
        }
    }
}

View

Viewはユーザーの目に実際に触れる部分を司っている要素です。View自体にデータは保持しておらず、Modelのデータを参照することでページを構築していき、Modelのデータが変更になれば都度表示も切り替えることで動的に変化するページが作成されます。

またユーザーと直接やり取りするインタラクティブ(対話的)な部分でもあります。ユーザーの入力操作やタップ操作などのイベントを検知します。

具体例:アニメーション、ユーザーインタラクションなどUI関連


import SwiftUI
import RealmSwift

struct EntryButtonView: View {
    
    // MARK: - ViewModels
    private let notificationViewModel = NotificationViewModel()
    private let realmDataBaseViewModel = RealmDataBaseViewModel()
    private let displayDateViewModel = DisplayDateViewModel()
    
    // MARK: - receive data
    public var notice:Notification? = nil
    
    // MARK: - Input
    @Binding  var date:Date
    @Binding  var text:String
    
    var body: some View {
        Button(action: {
            
            if !text.isEmpty {
                var currntItem:Notification
                if notice != nil {
                    currntItem = realmDataBaseViewModel.updateRecord(id:notice!.id,body: text, date: date)
                }else{
                    currntItem = realmDataBaseViewModel.createRecord(body: text, date: date)
                }
                
                let dateStr = displayDateViewModel.getNoticeFormatString(currntItem.date)
                notificationViewModel.createNotification(id: currntItem.id, body: currntItem.body, dateStr:dateStr)
            }
        }, label: {
            Text(notice != nil ? "更新" : "登録")
                .fontWeight(.bold)
                .padding(10)
        })
    }
}

ViewModel

ViewModelはViewとModelの仲介を行う部分です。Viewに表示させるためのデータをModelから受け取る役割や逆にViewから受け取ったら入力を適切にModelへと伝達する役目を待っています。Viewとはデータバインディングなどの仕組みを利用して同期的なデータの更新を実装します。

具体例:データのフィルタリング、ソート、および検索などをしてViewに渡す


import UIKit
import RealmSwift

class RealmDataBaseViewModel {
    
    // MARK: - Models
    private let model = RealmDataBaseModel()
    
    // MARK: - Create
    public func createRecord(body:String,date:Date) -> Notification{
        let notice = Notification()
        notice.body = body
        notice.date = date
        model.createRecord(notice: notice)
        return notice
    }
    
    // MARK: - Update
    public func updateRecord(id:UUID,body:String,date:Date) -> Notification{
        let noticeRecord = model.readIdRecord(id: id)
        model.updateRecord(noticeRecord: noticeRecord, body: body, date: date)
        let newNoticeRecord = model.readIdRecord(id: noticeRecord.id)
        return newNoticeRecord
    }
    
    // MARK: - Delete
    public func deleteAllOldRecord(){
        model.deleteAllOldRecord()
    }
}

MVVMの特徴とメリット

MVVMの1番の特徴はデータバインディングです。データバインディングとはデータと対応するオブジェクトを紐づけることで同期的な仕組みを持たせることで、データの変更がそのままビューにも適応されるような設計を構築できます。

データバインディングには単方向バインディング双方向バインディングがありますがこれは片側への反映のみか相互に反映させるかの違いです。

RxSwiftとMVVM

Swiftで MVVMに倣った設計を実装するためによく利用されるライブラリがRxSwiftです。MVVMの肝となるデータバインディングやイベントの検知など、様々な機能を実装できリアクティブプログラミングな開発を手助けしてくれます。

RxSwiftは学習コストが高く使いこなせるようになるまでには時間がかかりますが実践でもよく使われているライブラリなので学習して損はないような気がします。

おすすめ記事:RxSwiftとは?導入方法と使い方まとめ!ストリームを理解する

MVVM設計を実装する際のポイント

まとめると・・・

ModelとViewModelの関係は、Modelがアプリケーションの状態やデータを管理し、ViewModelがViewとModelの間の仲介を行い、Viewがユーザーインタフェースを提供する役割を担う

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index