LoginSignup
30
18

More than 3 years have passed since last update.

SwiftUI プログラムへの Core Data データベース機能の追加

Last updated at Posted at 2020-10-21

本記事では、

  • Core Data データベースを既存の SwiftUI アプリケーションに追加する方法を紹介します。
  • NSMergePolicy を使用したコアデータデータベースのマージ競合の管理方法についても説明します。

Core Dataモデルファイルとクラス定義の作成

モデルファイルとクラス定義用のコードファイルの作成の方法はこちらの通りです。これに関しては過去記事の1つで紹介済みです。

詳細に関してはこちらをクリックしてください

最初に、データモデルファイルを作成します。キーボードのcommand-Nキーを押して、Data Model を検索します。

Screen Shot 2020-05-27 at 2.37.09 PM.png

新しい Data Model にあなたのアプリの名前をつけてください。例えば、ここでは私のアプリの名前は CoreDataDemo なので、私は CoreDataDemo.xcdatamodeld を作りました。

データベース構造の作成

To-Doアイテムごとに次のプロパティを格納するTo-Doアプリを作成するとします:

  • To-Doタスク名
  • タスクの担当者
  • タスクの期限
1. .xcdatamodeld ファイルを開き、Add Entity をクリックします。

Screen Shot 2020-05-27 at 2.42.46 PM.png

作成した新規エンティティ・アイテムに TodoItem という名前を付けます。

作成した新しいアイテムをダブルクリックし、名前を付けるだけです。この例では、 TodoItem という名前を付けます。

新しいプロパティを追加する

Properties セクションのプラスアイコンをクリックして、新しいプロパティを追加します。

Screen Shot 2020-05-27 at 2.44.30 PM.png

ここでは、To-Doアプリに次のプロパティが必要です。

プロパティ名 タイプ
todoTaskName String
personName String
taskDueTime Date

タイプを設定するには、プロパティ名の横にあるドロップダウンメニューをクリックします:

Screen Shot 2020-05-27 at 2.47.09 PM.png

このような感じになります。

Screen Shot 2020-05-27 at 2.48.28 PM.png

Entity クラスのコードファイルを生成
  1. 作成した新しいエンティティ項目を選択します。右側のパネルで最後のセクションのアイコンを選択します :

Screen Shot 2020-05-27 at 3.05.23 PM.png

  1. Codegen 設定を Manual/ None に変更

以前の設定:

Screen Shot 2020-05-27 at 3.08.57 PM.png

新しい設定:

Screen Shot 2020-05-27 at 3.10.38 PM.png

  1. クラス定義ファイルを生成

最上部のメニューで Editor をクリックし、Create NSManagedObject subclass... をクリックします

こうしたサブクラスを生成することで、TodoItem を新規オブジェクトの作成に直接使用できるようになります。

Screen Shot 2020-05-27 at 3.11.37 PM.png

ファイルナビゲーターに以下のファイルが表示されます :

Screen Shot 2020-05-27 at 3.13.31 PM.png

データベースマネージャーを作成する

現在、コアデータベースに関連する関数を含む新規ファイルを作成できるようになっています。

このファイルで、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 関数に追加してください。SwiftUICoreData フレームワークのインポートをお忘れなく。

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)

また、変数 viewContextToDoItemCreate ビューに追加する必要があります。

@Environment(\.managedObjectContext) private var viewContext

データベースのコンフリクトへの対応

データベースが自身に変更を書き込もうとする際には、コンフリクトが発生することがあります。(例えばオブジェクトの重複など。)

デフォルトで、Core Data フレームワークは例外を発生させます。

viewContextmergePolicy プロパティーを設定することで、コンフリクトの解消を試みることができます。

self.viewContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType)

以下は一部の結合ポリシーの説明です。

Name Explanation
NSErrorMergePolicyType デフォルト。try viewContext.save() を呼び出すと、例外が報告されます。
NSMergeByPropertyStoreTrumpMergePolicyType メモリ内の変更は外部変更を置き換えます
NSMergeByPropertyObjectTrumpMergePolicyType 外部変更はメモリ内の変更を置き換えます。
NSOverwriteMergePolicyType メモリ内のオブジェクト全体が永続ストアにプッシュされます。
NSRollbackMergePolicyType 対立する変更されたオブジェクトについて、すべての状態が破棄されます

通常、NSMergeByPropertyObjectTrumpMergePolicy を使用すると、Core Data はデータベースのデータバージョンを新しい変更で自動的に上書きします。


:relaxed: Twitter @MszPro

:sunny: 私の公開されているQiita記事のリストをカテゴリー別にご覧いただけます。

30
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
18