CoreDataの勉強のため、Swiftで簡単なTodoアプリを作ってみた。
仕様について、UIはSwiftUIで一画面のビューを実装し、データの保存にCoreDataを使う。
それでは早速実装したコードを紹介しながら、各コードでどのような処理をしているかを解説していく。
コンテンツ
コード全文
まず、コード全文を載せておく。
import SwiftUI
import CoreData
struct ContentView: View {
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Task.name, ascending: false)],
animation: .default
)
var tasks: FetchedResults<Task>
@Environment(\.managedObjectContext) var viewContext
@State var new = ""
var body: some View {
VStack {
HStack {
TextField("Input here", text: $new)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
let newTask = Task(context: self.viewContext)
newTask.name = self.new
do {
try self.viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
self.new = ""
}) {
Text("追加")
}
}
List {
ForEach(tasks, id: \.self) { task in
Text("\(task.name!)")
}
}
}.padding()
}
}
なお、プロジェクト作成時には「Use Core Data」へチェックを入れておくことが前提だ。
また、事前に下記のとおりエンティティを定義しておく。
CoreDataからデータを取得する
CoreDataからデータを取得するには、簡単にデータを取り出せるようFetchRequestプロパティラッパー(コード5行目)が定義されている。
FetchRequestは、DynamicPropertyプロトコルを採用しているので、関連するビューのプロパティも自動更新してくれるようになっている。
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Task.name, ascending: false)],
animation: .default
)
今回の取得条件は、sortDescriptorsでTask.nameをキーとして降順(新しいタスクが上)でフェッチするように指定している。
フェッチした結果はFetchResults型として返されるので、以下のようにして取得する。
var tasks: FetchedResults<Task>
データベース操作で必要なコード
CoreDataを使ったデータベース操作にはNSManagedObjectContextのインスタンスが必要になるので、以下でインスタンスを取得する。(11行目)
@Environment(\.managedObjectContext) var viewContext
タスク追加用のフィールドとボタンを用意する
ここからはビューの実装が絡んでくる。(12行目以降)
まず、タスク名を入力するTextFieldを用意し、状態変数newをバインドさせておく。(17行目)
TextField("Input here", text: $new)
.textFieldStyle(RoundedBorderTextFieldStyle())
19行目でタスク追加ボタンのビューを用意しているが、このとき20行目からCoreDataへのデータ追加処理をおこなっている。
let newTask = Task(context: self.viewContext)
newTask.name = self.new
まずTaskクラスのインスタンスであるnewTaskを生成し、name属性に入力した値self.newを設定している。
このときTaskクラスのイニシャライザには、ManagedObjectContextを渡す必要がある。
そして22行目からデータベースへの保存処理をおこなっていく。
do {
try self.viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
saveメソッドはエラーを返す可能性があるので、必ずtry/catch文を使った例外処理を書いておく必要がある。
取得したデータの一覧表示
ここまで、CoreDataを使ってデータベースにデータを追加する処理を書いてきたが、次はCoreDataから取得してきたデータをListビューに一覧表示する処理を書いていく。
List {
ForEach(tasks, id: \.self) { task in
Text("\(task.name!)")
}
}
35行目でListビューを定義しており、ForEach文を使ってあらかじめ取得しておいたtasksを出力している。
Todoアプリの完成
完成したアプリをビルドした結果がこちら。
追加したタスクはリストにどんどんスタックされていき、アプリをいったん落とし再起動しても、追加したタスクが残っている。
この簡易Todoアプリをベースに少しずつ改良を加え、CoreDataへの理解を深めていこうと思う。