[SwiftUI][CoreData] SwiftUI と MVVM で始める CoreData 入門 (その2:MVVM アーキテクチャ導入)

SwiftUI と CoreData を組み合わせたアプリの作り方を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Catalina 10.15.7
  • Xcode 12.2
  • iOS 14.2

Xcode からテンプレートして作成されるコードは、MVVM とは言い難いので、MVVM アーキテクチャになるよう修正していきます。

MVVM 視点で修正したい箇所

MVVM では、View は、ViewModel のみを知っていることが前提ですが、テンプレートで生成されたコードには、ContentView に、CoreData が直接出てくるので、NG です。

Environment に、ManagedObjectContext を設定できるようにしている時点で、Apple としては、CoreData を Model Layer に留めておく気はないのかもしれません。

修正していく方針

  • View は ViewModel 経由で Model にアクセスする
  • Model は、基本ロジック等の CoreData に依存しない部分と、保存等の CoreData に依存する部分を意識して分離する
  • @Published 等の仕組みで変更を伝播させることとして、@FetchRequest は当面使用しない

特に2つ目の点は、将来的に、モデルレイヤーの入れ替え、例えば、CoreData からRealm に切り替えようとした時に、作り直しになってしまうかどうかに大きな影響を与えます。

モデルを作る(TODOItem)

TODO のアプリなので、以下のプロパティを持つ要素を作ることにします。

  • ID (id:UUID)
  • タイトル (title:String)
  • 詳細 (detail:String)

Model も CoreData に非依存で作成したいので、Swift 的なモデルと CoreData 上のモデルの2つを作成します。
Swift 的な struct を、TODOItem とし、 CoreData 上の Entity を CDTODOItem としました。(CD = CoreData)

TODOItem code

CDTODOItem 定義
CDTODOItem 定義

モデルを作る(TODOItemStore)

TODOItem を束ねて保持する要素が欲しくなるので、TODOItemStore とします。

できれば、TODOItemStore も CoreData 非依存としたいのですが、Model を CoreData 非依存とすると、ViewModel で Model と CoreData を管理することになってしまいます。

ViewModel を CoreData 非依存にしたいので、TODOItemStore だけは、CoreData へのリファレンスを持つこととしました。

Model の中で、DB layer 含めて完結する形になります。

TODOItemStore

PersistentContainer から要素を読み込むために TODOItem.init と TODOItemStore.init を作ります。

TODOItem.init, TODOItemStore.init

ここまでで、モデルに対して、コンパイルできる環境は作れたので、Model に対して、基本機能の実装を始めます。

TDD で Model の実装をすすめる

MyTODOApp.swift や ContentView.swift を変更する前に、モデルに機能を実装していきます。

現時点では、モデルの定義のみ存在し、機能する API がありません。

要素作成テストの実装

まずは、要素を作成し Model に保存できるかを確認するテストを作ります。

Model のテスト用に、Model_Tests.swift を、MyTODOTest フォルダ下に作成します。

TODOItem を作成できることを確認する test_createItem_newItem_CorrectValues を以下のように書きました。

test_createItem_newItem_CorrectValues

メソッド定義すらしていないので、当然コンパイルエラーです。

TODOItemStore.createTODOItem 実装

TODOItem を作るメソッドを以下のように実装しました。

TODOItemStore.createTODOItem

上記のコードを追加すると、コンパイルできるようになり、テストもパスします。

現時点では、オンメモリの CoreData を使ってテストしているので、きちんと CoreData に保存されたかをテストする方法がありませんが、後日、追加することを検討します。

要素削除テストの実装

削除のテストも作ってみます。

test_removeItem_existingItem_shouldBeVanished

要素削除の実装

削除するメソッドは、以下のように実装しました。

TODOItemStore.removeTODOItem

上記の実装で、テストはパスします。

ここまでの実装で、プログラム的には、TODOItem を作成し、削除することができるようになりました。

次回は、UI を使ったテストとその実装を行っていきます。

まとめ:Xcode12.2 で CoreData プロジェクト:CoreData は、M(Model) に入れてしまうのが良い

モデルと CoreData の組み合わせ方がポイント
  • テンプレートは、MVVM になっていないので、修正が必要
  • CoreData は、Model 内に留めておくと、綺麗な MVVM を作りやすい
  • CoreData に依存する Model を限定的にしておくと、再利用しやすい

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です