SwiftUIでCore Dataを扱うための前提知識と、新規プロジェクトの作成方法について解説します。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.3.2
【iOS】14.3
【macOS】Big Sur バージョン 11.1
Core Dataで使う主要クラス
Core Dataを使う上で、最低限下記クラスの役割は理解しておいた方が良いです。
NSPersistentContainer(永続化コンテナ)
Core Dataを扱うための機能が全部入ったクラスで、アプリの起動時に生成されます。
NSManagedObjectContext(被管理オブジェクトコンテキスト)
レコードの生成、保存、削除といったデータベース操作を行うクラスです。
Core Dataで検索や生成したオブジェクトは全てこのクラスの管理化に置かれます。
アプリ起動時に生成され、環境変数@Environment(\.managedObjectContext)に登録されます。
NSManagedObject(被管理オブジェクト)
データベースの1レコードに対応するクラスです。
Core Data用プロジェクトの作成
Core Dataを使う為、新規プロジェクト作成のオプション設定は次のようにします。
Interface:SwiftUI
Life Cycle:SwiftUI App
Use Core Data:チェックを入れる
これにより、Core Dataに必要なファイルおよびクラスの生成処理が追加されます。
自動的に生成されるコードの解説
Core Data用プロジェクトを作成した際に自動で生成されるコードおよびファイルについて解説します。
逆にこれらを手動で設定すれば、後からCore Data用プロジェクトにもできます。
(プロジェクト名)App.swift
アプリの起動時に実行する内容が記述されているファイルです。
ここに、下記2つの処理が追加されています。
- NSPersistentContainerの初期化
- 環境変数@Environment(\.managedObjectContext)にNSManagedObjectContextを登録する処理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import SwiftUI @main struct SampleApp: App { let persistenceController = PersistenceController.shared var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, persistenceController.container.viewContext) } } } |
ContentView.swift
Core Data用のプロジェクトを生成すると、通常のHello Worldの代わりに、Core Dataの標準テンプレートコードが追加されます。標準テンプレートついては、【SwiftUI】Core Dataの使い方:標準テンプレートを読み解くで解説していますので、こちらを御覧ください。
基本的にばっさりと削除してOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
import SwiftUI import CoreData struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], animation: .default) private var items: FetchedResults<Item> var body: some View { List { ForEach(items) { item in Text("Item at \(item.timestamp!, formatter: itemFormatter)") } .onDelete(perform: deleteItems) } .toolbar { #if os(iOS) EditButton() #endif Button(action: addItem) { Label("Add Item", systemImage: "plus") } } } private func addItem() { withAnimation { let newItem = Item(context: viewContext) newItem.timestamp = Date() do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } } } private func deleteItems(offsets: IndexSet) { withAnimation { offsets.map { items[$0] }.forEach(viewContext.delete) do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } } } } private let itemFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .medium return formatter }() |
(プロジェクト名).xcdatamodeld(追加ファイル)
データベースのテーブル構造(データモデル)を定義するファイルです。
デフォルトで定義されている、Itemエンティティは不要なので削除します。
Persistence.swift(追加ファイル)
NSPersistentContainerの初期化について記述されているファイルです。
コードの強調部分がpreview時の初期データを設定している部分ですで、一旦削除して自アプリ用に書き換えます。
尚、preview時に初期データ設定無しで、対象テーブルをFetchしようとするとアプリがcrashしますので注意して下さい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import CoreData struct PersistenceController { static let shared = PersistenceController() static var preview: PersistenceController = { let result = PersistenceController(inMemory: true) let viewContext = result.container.viewContext for _ in 0..<10 { let newItem = Item(context: viewContext) newItem.timestamp = Date() } do { try viewContext.save() } catch { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } return result }() let container: NSPersistentContainer init(inMemory: Bool = false) { container = NSPersistentContainer(name: "SampleProject") if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { // Replace this implementation with code to handle the error appropriately. // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. /* Typical reasons for an error here include: * The parent directory does not exist, cannot be created, or disallows writing. * The persistent store is not accessible, due to permissions or data protection when the device is locked. * The device is out of space. * The store could not be migrated to the current model version. Check the error message to determine what the actual problem was. */ fatalError("Unresolved error \(error), \(error.userInfo)") } }) } } |