[SwiftUI][CoreData] SwiftUI と MVVM で始める CoreData 入門 (その14:CoreData を Realm と入れ替え)

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

環境&対象

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

  • maxOS Catalina 10.15.7
  • Xcode 12.3
  • iOS 14.2

CoreData を Realm に入れ替える

MVVM(Model-View-ViewModel) で作ってきました。Model 入れ替えも簡単にできるハズなので、CoreData を Realm に入れ替えてみます。

CoreData を Realm に置き換える方針

View/ViewModel にできるだけ変更を入れないで対応してみます。

CoreData を Realm に置き換え時に課題になりそうな点

ざっくりとは、CoreData と Realm の相違点をチェックせずに作った MVVM なので、どこまで共有できるか不明です

以下は、少し調べて判明した 対応課題 です。

Model アップデート通知
現在は、NotificationCenter の NSManagedobjectContext 依存の通知を使っています
NSSortDescriptor
Realm は、独自の SortDescriptor を使います (NSSortDescriptor 自体は、Foundation に含まれるクラスです)
freeze
SwiftUI では、リスト等の表示に使われる配列は、変更されない(immutable) である必要があります。Realm には、そのために Results 型に freeze メソッドが用意されています

上記の点にも留意しながら修正していきます。

CoreData の Realm への入れ替え作業

ターゲット追加

まずは、新しい iOS App ターゲットを追加します。

名前は、”MyRealmTODO” としました。

Realm を SwiftPM で追加

SwiftPM 経由で Realm を追加します。

[File]-[Swift Packages]-[Add Package Dependencies…] 経由で、RealmSwift の URL “https://github.com/realm/realm-cocoa” を指定します。

追加対象のターゲットの確認ウィンドウで、先ほど作成したターゲット “MyRealmTODO” を選択します。(Realm, RealmSwift 共に指定します)

Realm 用 TODOItem 定義

Realm では、Object を継承したクラスで、モデル定義する必要があり、以下のようになります。

RealmTODOItem

コード解説
  1. Realm では、UUID を型としてサポートしていないので、uuidString メソッドで String 化した値を保持することにします。
  2. UUID は、ユニークなので、primaryKey に使うよう設定します。
  3. Realm は、reflection で情報を取得するので、init の実装が必要です。
  4. 通常の init です

RealmTODOItem から TODOItem を作る init も定義します。

TODOItem.init

Realm 用 TODOItemStore の定義

実際には、TODOItemStore が DB layer とのやりとりを行いますので、ほとんど書き直しとなります。

CoreData 用の TODOItemStore は、Realm 用のターゲットには含めませんので、同じ名称で作成します。(ViewModel を変更しなくて済みます)

TODOItemStore
コード解説
  1. CoreData 用 ViewModel と同じインターフェースでメソッドを用意します
  2. 内部向けに メソッドを用意します
  3. CoreData 用 ViewModel と同じインターフェースでメソッドを用意します
  4. Realm は、NSSortDescriptor を扱えないので、Realm の扱うことのできる SortDescriptor に変換して使います
  5. CoreData 用 TODOItemStore と同じインターフェースでメソッドを用意します
  6. CoreData 用 TODOItemStore と同じインターフェースでメソッドを用意します
  7. Realm を fetch するケースが多かったので、別メソッドを作りました
  8. CoreData 用 TODOItemStore と同じインターフェースでメソッドを用意します
  9. ViewModel は、NotificationCenter からの Notification.Name.NSManagedObjectContextObjectsDidChange 通知を受けて更新しているので、Realm モデル更新時にも同じ 通知を送っています。(もう少し改善する余地がありそうです)
  10. CoreData 用 TODOItemStore と同じインターフェースでメソッドを用意します
  11. CoreData 用 TODOItemStore と同じインターフェースでメソッドを用意します
  12. CoreData 用 TODOItemStore と同じインターフェースでメソッドを用意します
  13. Realm では、Realm の要素プロパティを偏向時に反映されるため、save は不要です

NSSortDescriptor と SortDescriptor の対応

上記でも説明していますが、Realm は、NSSortDescriptor は使わずに、独自の SortDescriptor を使ってソートを行います。

その変換のために、以下の Extention も作成しました

NSSortDescriptor extension for supporting SortDescriptor

Realm は、Undo/Redo をサポートする仕組みを持っていないので、自前で実装する必要があります。

それまでは、非対応ということで、以下の空メソッドを実装します。

TODOItemStore extension for UNDO/REDO

上記のように実装することで、ViewModel, View だけではなく、App も共用することができました。

まとめ:CoreData を Realm と入れ替える時の懸念点とその対応

MVVM で作ると、非常に簡単に DB Layer を入れ替えることが確認できました。

CoreData を Realm と入れ替える時の懸念点とその対応
  • Realm では、UUID に対応していないので、uuidString を使って、String として保存
  • id を PrimaryKey に設定すると良い
  • Realm は、NSSortDescriptor に対応していないので、SortDescriptor に変換して使用する
  • Realm では、リフレクションで情報を取得するため 引数なしの init 実装が必要
  • MVVM で作成していれば、View に提供された情報は、直接のモデル情報ではないので、freeze を使う必要はなかった

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

おすすめの Realm 本

Web で API 等を Reference で確認することができますが、じっくりと腰を据えて学習したいときには、学習の順番が考慮されている本を使うのがおすすめです。

Realm については、本がほとんど出版されていませんが、以下の本は おすすめです。

コメントを残す

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