[WWDC2023] SwiftDataを使ってみた。

CoreDataの後継者であるSwiftDataがついにリリースされたので、簡単なアプリで使ってみた。
SwiftDataを導入するには、6つの簡単なステップを踏む。
import SwiftData- モデル作成
-
AppでmodelContainer作成 -
@Environment(\.modelContext)を宣言 -
@Queryを配列の変数につける - モデルを
contextにinsertしてsave()を行えば完成
それでは、ひとつずつ見ていこう。
1. SwiftData移入
まずはimportしよう
import SwiftData
2. モデル作成
CoreDataを使う場合モデル作成するには.xcdatamodelファイルを開いて、「Add Entity」でEntityを追加するように実装していたが、SwiftDataではローカルデータを使用しないようにclassを作り、その上に@Modelを記述するだけです。 @Modelを使用して、通常のSwiftの型でデータをモデル化することができる。
@Model
class Restaurant {
let name: String
var dateAdded: Date
init(name: String, dateAdded: Date = .init()) {
self.name = name
self.dateAdded = dateAdded
}
}
3. Appで.modelContainer()
CoreDataを実装するみたいにAppにcontainerを注入する。.modelContainer()という新しいメソッドで環境に注入できる。2.で宣言したRestaurantを指定する。
@main
struct SwiftDataPracticeApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: Restaurant.self)
}
}
4. @Environment(\.modelContext)を宣言
次に、modelContextをViewに宣言します。DBにデータを挿入する際には、contextが必要になる。
struct ContentView: View {
@Environment(\.modelContext) var context
// ...
}
5. @Queryを配列の変数につける
SwiftUIのビューで@Queryを使用してデータをフェッチすることができる。SwiftDataとSwiftUIは連携して動作し、基になるデータが変更されると、ビューに自動的に最新の情報が反映される。結果を手動でリフレッシュする必要はない。
@Query private var favourites: [Restaurant]
CoreDataのようにSortDescriptorとPredicateが使える。
下記の場合、Restaurantが.reverse順番で表示する。
@Query(FetchDescriptor(sortBy: [SortDescriptor(\.dateAdded, order: .reverse)]),animation: .snappy) private var favourites: [Restaurant]
6. モデルをcontextにinsertとsaveしよう
contextにinsertとsaveしよう!
モデルをcontextに挿入した後は、保存することを忘れないでください。これは2つのステップからなるプロセスで、contextトに対して行われた変更は、保存することで確定される。
// ...
Button("Add Item") {
insertIntoContext()
}
// ...
private func insertIntoContext() {
let restaurant = Restaurant(name: "Hello User \(Date().formatted(date: .numeric, time: .shortened))")
context.insert(restaurant)
saveContext()
}
private func saveContext() {
do {
try context.save()
} catch {
print(error.localizedDescription)
}
}
それで、 SwiftDataが使えてローカルデータで保存できた!
スクショ

使ったコード
ContentView.swift
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) var context
@Query(FetchDescriptor(sortBy: [SortDescriptor(\.dateAdded, order: .reverse)]),
animation: .snappy) private var restaurants: [Restaurant]
var body: some View {
NavigationStack {
List(restaurants) { restaurant in
HStack {
Text(restaurant.name)
.font(.caption)
Spacer()
}
}
.navigationTitle("SwiftData")
.toolbar(content: {
ToolbarItem(placement: .topBarTrailing) {
Button("Add Item") {
insertIntoContext()
}
}
})
}
}
private func insertIntoContext() {
let restaurant = Restaurant(name: "\(Date().formatted(date: .numeric, time: .standard))にレストランで食べた")
context.insert(restaurant)
saveContext()
}
private func saveContext() {
do {
try context.save()
} catch {
print(error.localizedDescription)
}
}
}
Restaurant.swift
@Model
class Restaurant {
let name: String
var dateAdded: Date
init(name: String, dateAdded: Date = .init()) {
self.name = name
self.dateAdded = dateAdded
}
}
SwiftDataPracticeApp.swift
import SwiftUI
@main
struct SwiftDataPracticeApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: Restaurant.self)
}
}
ここまで読んでくれてありがとうございました。
Spacely, Inc. App Div.
Dean Thompson
参考
Discussion
「3. Appで.modelContainer()」にて「.modelContainer()という新しいメソッドで環境に注入できる。2.で宣言したRestaurantを指定する。」という記述があります。サンプルコードとしては以下のコードが示されています。
このサンプルコードでは
modelContainerにPerson.selfが渡されていますが、ここがRestaurant.selfになるのが正しいでしょうか?コメント、ありがとうございます!
そうですね。合っています!
合っていない箇所を更新しました。
ありがとうございます🙇♂️