[WWDC2023] SwiftDataを使ってみた。
CoreData
の後継者であるSwiftData
がついにリリースされたので、簡単なアプリで使ってみた。
SwiftData
を導入するには、6つの簡単なステップを踏む。
import SwiftData
- モデル作成
-
App
でmodelContainer
作成 -
@Environment(\.modelContext)
を宣言 -
@Query
を配列の変数につける - モデルを
context
にinsert
してsave()
を行えば完成
それでは、ひとつずつ見ていこう。
SwiftData
移入
1. まずは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
}
}
.modelContainer()
3. AppでCoreData
を実装するみたいにApp
にcontainerを注入する。.modelContainer()
という新しいメソッドで環境に注入できる。2.で宣言したRestaurant
を指定する。
@main
struct SwiftDataPracticeApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: Restaurant.self)
}
}
@Environment(\.modelContext)
を宣言
4. 次に、modelContext
をView
に宣言します。DBにデータを挿入する際には、contextが必要になる。
struct ContentView: View {
@Environment(\.modelContext) var context
// ...
}
@Query
を配列の変数につける
5. 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になるのが正しいでしょうか?コメント、ありがとうございます!
そうですね。合っています!
合っていない箇所を更新しました。
ありがとうございます🙇♂️