#〜.xcdatamodeldファイルを生成する。
プロジェクト作成時にCoreDataを有効にしている場合は自動で作成されてます。後から追加する場合はこのファイルが必須になります。CoreDataはコマンドラインやxmlを使わずに、このフォームベースでデータベースの情報が登録できます。そのためには.xcdatamodeldが必須になります。
右クリックからNew Fileを選択して
下にスクロールしてCore DataのData Modelを選択してください。
#保存したいデータの情報を登録する。
EntityとAttribute
Entityにはどんな情報を管理したいのかわかるような名前をつけます。タスク管理ならTask、メモ帳ならMemoなど、表の題名をイメージするとわかりやすいです。
Attributeには、Entityに保存する情報の単位とデータ型を決めます。タスクを新規追加した時間(Date)、タスク名(String)、タスクの有効/無効(Boolean)など、表の列名をイメージするとわかりやすい。
NSManagedObjectを継承したクラスを作成する。
先程のCoreDataに作ったEntityとAttributeをSwiftからアクセスしてオブジェクトとして利用するためにNSManagedObjectを継承したデータクラスを作成します。
〜.xcdatamodeldを選択した状態でEditor->Create NSManagedObject Subclass...を選択して
案内通りに進みます。
Task+CoreDataClass.swiftとTask+CoreDataProperties.swiftが生成されます。
デフォルトの設定だと、NSManagedObjectを継承したクラスが存在しなくてもビルド時にxcodeが生成してくれますが、後々いろいろとやるために自分で作成して管理したほうが楽だと思いますので、この設定を無効にするためにcodegen(コードジェネレータ)をManual/Noneに変更します。
リスト形式でデータを表示するためにIdentifiableを継承させます。
import Foundation
import CoreData
extension Task {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Task> {
return NSFetchRequest<Task>(entityName: "Task")
}
@NSManaged public var id: UUID?
@NSManaged public var name: String?
@NSManaged public var isComplete: Bool
@NSManaged public var dateAdded: Date?
}
//Identifiableに対応させるため追加
extension Task: Identifiable {
}
AppDelegate.swift
CoreDataライブラリを読み込ませ
import UIKit
import CoreData
AppDelegateクラスにpersistentContainerプロパティとsaveContextメソッドを追加
下のコードを追加します。
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Model")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
NSPersistentContainerのnameに渡す文字列は.xcdatamodeldのファイル名になります。
自分が定義したファイル名に書き換えないとエラーになりますので注意してください。
Model.xcdatamodeldの場合
let container = NSPersistentContainer(name: "Model")
SceneDelegate.swift
CoreDataからデータを取得したり、新規データを追加するには、Contextが必要になります。ContentViewからContextを利用するためSceneDelegate.swiftを修正します。
let contentView = ContentView()
を下に変更
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = ContentView().environment(\.managedObjectContext, context)
import SwiftUI
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(
entity: Task.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Task.dateAdded, ascending: false)],
predicate: NSPredicate(format: "isComplete == %@", NSNumber(value: false))
) var notCompletedTasks: FetchedResults<Task>
@State var newTaskName:String = ""
func addTask(taskName: String, context: NSManagedObjectContext) {
let task = Task(context: self.context)
task.id = UUID()
task.isComplete = false
task.name = taskName
task.dateAdded = Date()
do {
try self.context.save()
} catch {
print(error)
}
}
func completeTask(_ task: Task, context: NSManagedObjectContext){
let taskId = task.id! as UUID
print(taskId)
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Task")
fetchRequest.predicate = NSPredicate(format: "id == %@", taskId as CVarArg)
fetchRequest.fetchLimit = 1
do {
let tasks = try context.fetch(fetchRequest)
let targetTask = tasks[0] as! NSManagedObject
targetTask.setValue(true, forKey: "isComplete")
try context.save()
print(targetTask)
} catch {
print(error)
}
}
var body: some View {
VStack {
TextField("タスクを入力してください。", text: $newTaskName, onEditingChanged: { _ in
}, onCommit: {
self.addTask(taskName: self.newTaskName, context: self.context)
self.newTaskName = ""
})
.keyboardType(.default)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(32)
List {
ForEach(self.notCompletedTasks){ task in
Button(action: {
self.completeTask(task, context: self.context)
}) {
Text(task.name!)
}.onAppear() {
print(task.isComplete)
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}