本記事では、
- Core Data データベースを既存の SwiftUI アプリケーションに追加する方法を紹介します。
-
NSMergePolicyを使用したコアデータデータベースのマージ競合の管理方法についても説明します。
Core Dataモデルファイルとクラス定義の作成
モデルファイルとクラス定義用のコードファイルの作成の方法はこちらの通りです。これに関しては過去記事の1つで紹介済みです。
詳細に関してはこちらをクリックしてください
最初に、データモデルファイルを作成します。キーボードのcommand-Nキーを押して、Data Model を検索します。
新しい Data Model にあなたのアプリの名前をつけてください。例えば、ここでは私のアプリの名前は CoreDataDemo なので、私は CoreDataDemo.xcdatamodeld を作りました。
データベース構造の作成
To-Doアイテムごとに次のプロパティを格納するTo-Doアプリを作成するとします:
- To-Doタスク名
- タスクの担当者
- タスクの期限
1. .xcdatamodeld ファイルを開き、Add Entity をクリックします。
作成した新規エンティティ・アイテムに TodoItem という名前を付けます。
作成した新しいアイテムをダブルクリックし、名前を付けるだけです。この例では、 TodoItem という名前を付けます。
新しいプロパティを追加する
Properties セクションのプラスアイコンをクリックして、新しいプロパティを追加します。
ここでは、To-Doアプリに次のプロパティが必要です。
| プロパティ名 | タイプ |
|---|---|
| todoTaskName | String |
| personName | String |
| taskDueTime | Date |
タイプを設定するには、プロパティ名の横にあるドロップダウンメニューをクリックします:
このような感じになります。
Entity クラスのコードファイルを生成
- 作成した新しいエンティティ項目を選択します。右側のパネルで最後のセクションのアイコンを選択します :
-
Codegen設定をManual/ Noneに変更
以前の設定:
新しい設定:
- クラス定義ファイルを生成
最上部のメニューで Editor をクリックし、Create NSManagedObject subclass... をクリックします
こうしたサブクラスを生成することで、TodoItem を新規オブジェクトの作成に直接使用できるようになります。
ファイルナビゲーターに以下のファイルが表示されます :
データベースマネージャーを作成する
現在、コアデータベースに関連する関数を含む新規ファイルを作成できるようになっています。
このファイルで、NSPersistentCloudKitContainer をご利用のデータベース名で初期化することになります。
import CoreData
struct PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentCloudKitContainer
init() {
container = NSPersistentCloudKitContainer(name: "CoreDataDemo")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
print(error.localizedDescription)
}
})
}
}
その後、メインのSwiftUIアプリファイル(@mainというコードから始まるファイル)で、PersistenceController の共有インスタンスを初期化できます。
@main
struct SimpleTODOApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
SceneDelegate を使っている場合には、SceneDelegate.swift ファイルにおいて、コードを scene 関数に追加してください。SwiftUI と CoreData フレームワークのインポートをお忘れなく。
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let context = PersistenceController.shared.container.viewContext
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
.environment を追加
これで、PersistenceController.shared がすでに初期化されているので、ルートSwiftUIビューの環境変数を設定する必要があります。
まず、コンテンツビューを初期化するファイルにこの行を追加します。
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
そして、この変数をコンテンツビュー ContentView の最上部に追加します:
@Environment(\.managedObjectContext) private var viewContext
これで、コンテンツビューがデータベースにアクセスできるようになります。
FetchRequestを追加する
データベースからコンテンツをフェッチするには、FetchRequest を作成します。
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \TodoItem.taskDueTime, ascending: true)],
animation: .default)
private var items: FetchedResults<TodoItem>
上記のコード例では、TodoItem.dueTime に基づいて項目をソートしています。それを自分のプロパティ名に変更する必要があります。
keyPath 変数のフォーマットを[データベースエンティティの名前].[選択したエンティティの変数名]にする必要があります。
これで、アイテムをテーブルに表示できます。データベース内の項目が変更されると、テーブルは自動的に更新されます。
その他のSwiftUIビューの環境の準備
初期化する各ビューに対してこのステップを繰り返して環境を準備することが重要です。
例えば、ToDoItemCreate というビューがあるなら、ToDoItemCreate ビューを初期化する際にはこの行を追加する必要があります。
ToDoItemCreate(isShown: $showTodoCreateView)
.environment(\.managedObjectContext, self.viewContext)
また、変数 viewContext を ToDoItemCreate ビューに追加する必要があります。
@Environment(\.managedObjectContext) private var viewContext
データベースのコンフリクトへの対応
データベースが自身に変更を書き込もうとする際には、コンフリクトが発生することがあります。(例えばオブジェクトの重複など。)
デフォルトで、Core Data フレームワークは例外を発生させます。
viewContext の mergePolicy プロパティーを設定することで、コンフリクトの解消を試みることができます。
self.viewContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType)
以下は一部の結合ポリシーの説明です。
| Name | Explanation |
|---|---|
| NSErrorMergePolicyType | デフォルト。try viewContext.save() を呼び出すと、例外が報告されます。 |
| NSMergeByPropertyStoreTrumpMergePolicyType | メモリ内の変更は外部変更を置き換えます |
| NSMergeByPropertyObjectTrumpMergePolicyType | 外部変更はメモリ内の変更を置き換えます。 |
| NSOverwriteMergePolicyType | メモリ内のオブジェクト全体が永続ストアにプッシュされます。 |
| NSRollbackMergePolicyType | 対立する変更されたオブジェクトについて、すべての状態が破棄されます |
通常、NSMergeByPropertyObjectTrumpMergePolicy を使用すると、Core Data はデータベースのデータバージョンを新しい変更で自動的に上書きします。
