【SwiftUI】Core Dataの使い方:基本編

SwiftUIでCore Dataの基本的な使い方を簡単なTodoリストアプリを例に解説します。

サンプルアプリ

スポンサーリンク

環境

この記事の情報は次のバージョンで動作確認しています。

【Xcode】12.4
【Swift】5.3.2
【iOS】14.4
【macOS】Big Sur バージョン 11.1
スポンサーリンク

新規プロジェクトの生成

Core Dataを使った新規プロジェクトの作成方法は、【SwiftUI】Core Dataの使い方:準備編を参照して下さい。
デフォルトで生成される標準テンプレートのコードとエンティティ(Item)は削除してしまってOKです。
Persistence.swift のpreview初期データ設定部分も忘れずに削除して下さい。

スポンサーリンク

データモデルの定義

Todoリストアプリに必要なデータモデルを定義します。
Model Editor を開いて、次の Task エンティティを作成します。

データモデルの定義

属性項目は次の3つです。

timestamp : タスクの作成日付(Date型)
checked:タスクがチェック済かどうかのフラグ(Boolean型)
name:タスク名(String型)

Model Editorの使い方は【SwiftUI】Core Dataの使い方:エンティエィ(Entity)を定義するで解説しています。

スポンサーリンク

Todoリストアプリの全コード

以下がTodoリストアプリの全コード(ContentView.swift)です。
Core Dataの操作に関する主要部分については、以降の節で解説します。

スポンサーリンク

データ取得処理

SwiftUIではデータベースの検索結果とViewを同期する為の大変便利な仕組みとしてプロパティラッパー(@FetchRequest)が用意されています。
@FetchRequestを使ってプロパティを宣言すると、プロパティに検索結果が格納されるとともに、データの変更に応じて検索結果が常に最新に保たれます。
このプロパティを使ってViewを生成すると、データの変更がViewに即時反映される仕組みです。

@FetchRequest(…) の部分がプロパティラッパーの定義、private var tasks… 以降がプロパティの宣言です。
@FetchRequestには、検索対象エンティティ(entity:)、ソート順(sortDescriptors:)、抽出条件(predicate:)などの引数が指定可能です。
検索結果が配置されるプロパティは FetchedResults<エンティティクラス> のコレクション型で、1レコードに該当するエンティティクラス(NSManagedObjectの派生クラス)の配列を保持します。

サンプルコードでは、Task エンティティを対象とし、抽出条件無し(=全て)、timestamp 属性の昇順でソートした検索結果を、プロパティ tasks に配置しています。

スポンサーリンク

新規登録処理

データの新規登録は、エンティティクラス(NSManagedObjectの派生クラス)のインスタンスを生成して実現します。
インスタンス生成時の引数contextにはNSManagedObjectContextを指定します。

サンプルコードではエンティティクラスTaskのインスタンスを生成後、初期属性値を設定しています。

これで、見かけ上は新たなレコードが追加されたような振る舞いをしますが、あくまでNSManagedObjectContext内にインスタンスが追加されただけにすぎません。追加したレコードをアプリが終了しても消えないよう永続化する為には、NSManagedObjectContextの変更をデータベースファイルに保存する必要があります。
RDBで言う、コミット処理です。

保存処理にはNSManagedObjectContextの save() メソッドを使います。
この処理はエラーを返す可能性があるためtryをつけて呼び出します。

サンプルコードでは try? でエラーを無視していますが、必要に応じてエラー処理を追記してください。

スポンサーリンク

変更処理

変更処理はエンティティインスタンスの属性を変更するだけです。
変更を永続化する為には、保存処理も忘れないようにしましょう。

サンプルコードでは、タスクセルをタップした時に、checked フラグを切り替えています。

スポンサーリンク

削除処理

削除処理にはNSManagedObjectContextの delete() メソッドを使います。
引数には削除するエンティティのインスタンスを指定します。
追加や変更時と同様に、削除後は保存処理を呼び出して下さい。

サンプルコードでは、セルの削除操作が行われた時にこの処理が呼び出され、対象のインスタンスを削除しています。

スポンサーリンク

条件付き保存処理

保存処理に使うNSManagedObjectContextの save() メソッドですが、一般的にはデータの挿入や削除など具体的な変更を行った後に呼ばれます。

しかし、すべての変更を施した後に一括で保存するようなケースもあります。
その場合は、save() を呼び出す前に、保存されていない変更が無いかチェックして必要のない作業をさせないようにするのが良いでしょう。

NSManagedObjectContextには hasChanges プロパティがあり、コンテキストが所有するオブジェクトに変更があるかどうかチェック可能です。

次のように使います。

スポンサーリンク

サンプルコードでpreviewが動かない

本サンプルコードはシミュレーターや実機では動くのですが、なぜか preview で実行しようとすると crash して動きません。
これは初期データが無い状態で、対象テーブルを Fetch しようした時に発生します。(Xcodeの不具合だと思うのですが・・)

これは、preview用の初期データを用意すると回避できます。

Persistence.swift の次の箇所に preview用初期データ登録処理を追加し、cmd+B で再Buildして下さい。
resumeでは無くて再Buildです。

スポンサーリンク

合わせて読みたい記事

【SwiftUI】Core Dataの使い方:標準テンプレートを読み解く
(2023/03/25 更新) SwiftUIでのCore Dataの使い方を、標準テンプレートを読み解きながら解説します。
【SwiftUI】Core Dataの使い方:エンティエィ(Entity)を定義する
(2021/04/26 更新) Xcodeでエンティティを定義する方法を解説します。
【SwiftUI】Core Dataの使い方:準備編
SwiftUIでCore Dataを扱うための前提知識と、新規プロジェクトの作成方法について解説します。
【SwiftUI】Core Dataの使い方:検索編(1/3)
データベースを検索して、オブジェクトとして取得するfetch処理について解説します。 を先に見ておくことをおすすめします。
【SwiftUI】Core Dataの使い方:検索編(2/3)
検索条件を動的に変更する方法を解説します。 の続編です。
【SwiftUI】Core Dataの使い方:検索編(3/3)
前の2つの記事 では、SwiftUIの特徴であるデータバインディングの仕組みを使い、検索結果とViewを紐付ける方法を紹介しました。これにより、検索結果に応じてViewが自動で再描画されます。 しかし、検索結果を別の処理のインプットに使う場...
【SwiftUI】Core Dataの使い方:リレーションシップ編
リレーションシップを使ってエンティティ同士をリンクする方法を解説します。