SwiftUI 應用程式開發的常見問題是如何使用 Core Data 將資料永久保存在內建資料庫中。 儘管 Apple 不斷努力簡化 Core Data 的 API,但新來者常常發現框架使用起來很困難。 然而,好消息即將到來。 蘋果將在 iOS 17 中發布名為 SwiftData 的新框架來取代 Core Data。 SwiftData 旨在更易於用於資料建模和管理,提供更使用者友善的方法。
首先,需要注意的是,SwiftData 框架不應與資料庫混淆。 SwiftData 建構在 Core Data 之上,實際上是一個框架,旨在幫助開發人員管理持久性儲存上的資料並與之互動。 雖然 iOS 的預設持久性儲存通常是 SQLite 資料庫,但值得注意的是,持久性儲存也可以採用其他形式。 例如,Core Data 也可以用於管理本機檔案中的數據,例如 XML 檔案。
無論你使用的是 Core Data 還是 SwiftData 框架,這兩種工具都可以幫助開發人員免受底層持久儲存的複雜性的影響。 以 SQLite 資料庫為例。 使用 SwiftData,無需擔心連接資料庫或理解 SQL 來檢索資料記錄。 相反,開發人員可以專注於使用 API 和 Swift 巨集(例如 @Query
和 @Model
)來有效管理應用程式中的資料。
iOS 17 中新引入了 SwiftData 框架,以取代先前稱為 Core Data 的框架。 自 Objective-C 時代以來,Core Data 一直是 iOS 開發的資料管理 API。 儘管開發人員可以將該框架整合到 Swift 專案中,但 Core Data 並不是 Swift 和 SwiftUI 的本機解決方案。
在最新版的 iOS 中,Apple 終於為 Swift 引入了一個名為 SwiftData 的原生框架,用於持久資料管理和資料建模。 它建立在 Core Data 之上,但 API 完全重新設計以充分利用 Swift。
如果你曾使用過 Core Data,可能會記得必須使用資料模型編輯器建立資料模型(檔案副檔名為 .xcdatamodeld)以實現資料持久性。 隨著 SwiftData 的發布,你不再需要這樣做。 SwiftData 使用巨集(這是 iOS 17 中的另一個新 Swift 功能)簡化了整個過程。例如,你已經為 Song 定義了一個模型類,如下所示:
class Song {
var title: String
var artist: String
var album: String
var genre: String
var rating: Double
}
若要使用 SwiftData,新的 @Model
巨集是使用 SwiftUI 儲存持久資料的關鍵。 SwiftData 不需要使用模型編輯器建立資料模型,而是只需要使用 @Model
巨集註解模型類,如下所示:
@Model class Song {
var title: String
var artist: String
var album: String
var genre: String
var rating: Double
}
這就是在程式碼中定義資料模型架構的方式。 透過這個簡單的關鍵字,SwiftData 會自動啟用資料類別的持久性,並提供其他資料管理功能,例如 iCloud 同步。 屬性是從屬性推斷出來的,它支援基本值類型,例如 Int 和 String。
SwiftData 允許您使用屬性元資料自訂架構的建置方式。 你可以使用 @Attribute
註解新增唯一性約束,並使用 @Relationship
註解刪除傳播規則。 如果你不想包含某些屬性,可以使用 @Transient
巨集告訴 SwiftData 排除它們。 這是一個例子:
@Model class Album {
@Attribute(.unique) var name: String
var artist: String
var genre: String
// The cascade relationship instructs SwiftData to delete all
// songs when the album is deleted.
@Attribute(.cascade) var songs: [Song]? = []
}
為了驅動資料持久化操作,你需要熟悉 SwiftData 的兩個關鍵物件:「ModelContainer」和「ModelContext」。 「ModelContainer」充當模型類型的持久後端。 要建立 ModelContainer
,你只需實例化它的一個實例。
// Basic
let container = try ModelContainer(for: [Song.self, Album.self])
// With configuration
let container = try ModelContainer(for: [Song.self, Album.self],
configurations: ModelConfiguration(url: URL("path"))))
在 SwiftUI 中,你可以在應用程式的根目錄中設定模型容器:
import SwiftData
import SwiftUI
@main
struct MusicApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer (for: [Song.self, Album.self]))
}
}
設定模型容器後,就可以開始使用「model context」來取得和儲存資料。 「model context」可用作追蹤更新、獲取資料、保存變更甚至撤消這些更改的介面。 使用 SwiftUI 時,通常可以從視圖環境中取得「model context」:
struct ContextView: View {
@Environment(\.modelContext) private var modelContext
}
有了「model context」,你就可以獲得數據了。 最簡單的方法是使用 @Query
屬性包裝器。 您可以使用一行程式碼輕鬆載入和過濾資料庫中儲存的任何內容。
@Query(sort: \.artist, order: .reverse) var songs: [Song]
若要在持久性儲存中插入項目,你可以呼叫「model context」的 insert 方法並向其傳遞要插入的模型物件。
modelContext.insert(song)
同樣,你可以透過「model context」刪除該項目,如下所示:
modelContext.delete(song)
這是 SwiftData 的簡單介紹。 如果你仍然對如何使用 SwiftData 感到困惑? 不用擔心。 建立 ToDO 應用程式後你將了解其用法。
現在你已經對 SwiftData 有了基本的了解,我想示範如何使用此框架建立一個簡單的待辦事項應用程式(ToDo App)。 請注意,此應用程式功能不完整,僅允許用戶將隨機任務新增至待辦事項清單。 然而,它是熟悉 SwiftData 框架的一個很好的起點。
假設你已經在 Xcode 中建立了一個 SwiftUI 項目,我們首先建立應用程式的資料模型。 建立一個名為ToDoItem
的新檔案並更新內容,如下所示:
import Foundation
import SwiftData
@Model class ToDoItem: Identifiable {
var id: UUID
var name: String
var isComplete: Bool
init(id: UUID = UUID(), name: String = "", isComplete: Bool = false) {
self.id = id
self.name = name
self.isComplete = isComplete
}
}
如前所述,SwiftData 簡化了使用程式碼定義模式的過程。 你所需要做的就是使用 @Model
巨集註解模型類別。 然後 SwiftData 就會自動啟用資料類別的持久性。
在我們繼續建立 App UI 並處理持久性資料之前,讓我們先建立一個輔助函數來產生隨機待辦事項:
func generateRandomTodoItem() -> ToDoItem {
let tasks = [ "Buy groceries", "Finish homework", "Go for a run", "Practice Yoga", "Read a book", "Write a blog post", "Clean the house", "Walk the dog", "Attend a meeting" ]
let randomIndex = Int.random(in: 0..<tasks.count)
let randomTask = tasks[randomIndex]
return ToDoItem(name: randomTask, isComplete: Bool.random())
}
接下來,讓我們建立待辦事項應用程式的主 UI。 在 ContentView.swift
檔案中,更新程式碼如下:
import SwiftData
struct ContentView: View {
@Query var todoItems: [ToDoItem]
var body: some View {
NavigationStack {
List {
ForEach(todoItems) { todoItem in
HStack {
Text(todoItem.name)
Spacer()
if todoItem.isComplete {
Image(systemName: "checkmark")
}
}
}
}
.navigationTitle("To Do List")
}
}
}
我們使用 @Query
屬性包裝器標記 todoItems
數組。 此 @Query
屬性會自動為您取得所需的資料。 在提供的程式碼中,我們指定取得 ToDoItem
實例。 一旦我們檢索到待辦事項,我們就會利用 List
視圖來顯示這些項目。
為了驅動資料持久化操作,我們還需要設定模型容器。 切換到 ToDoDemoAppApp.swift
並附加modelContainer
修飾器,如下所示:
struct ToDoDemoAppApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: ToDoItem.self)
}
}
在這裡,我們設定一個共享模型容器來儲存 ToDoItem
的實例。
如果你預覽 ContentView
,則清單視圖為空。 顯然,我們沒有在資料庫中儲存任何待辦事項。 現在,讓我們新增一個「Add Item」按鈕將隨機待辦事項插入資料庫。
在 ContentView.swift
中,宣告下列變數來檢索模型上下文:
@Environment(\.modelContext) private var modelContext
獲得 Model Context 後,我們可以輕鬆地將資料插入資料庫。 我們將新增一個工具列按鈕來新增隨機待辦事項。 在 NavigationStack
視圖中插入以下程式碼(將其放在 navigationTitle
之後):
.toolbar {
Button("", systemImage: "plus") {
modelContext.insert(generateRandomTodoItem())
}
}
要將項目儲存到資料庫中,你只需呼叫模型上下文的 insert
方法即可。
現在你已準備好在模擬器中測試 App。 但是,如果你打算在預覽畫布中測試它,則需要透過在「#Preview」區塊中新增模型容器來進行額外的修改:
#Preview {
ContentView()
.modelContainer(for: ToDoItem.self)
}
當你點擊「+」按鈕時,應用程式會立即儲存待辦事項。 同時,它從資料庫中檢索新項目並將其顯示在清單視圖中。
SwiftData 簡化了處理持久性儲存的程序,所需要修改的程式碼也顯著減少。 只需使用 @Model
巨集標記模型對象,SwiftData 就會自動修改設定器以進行更改追蹤和觀察。 這意味著無需更改程式碼即可更新待辦事項。
要測試待辦事項更新,你只需在模擬器上執行 App 即可。 當你點擊待辦事項時,它應該被標記為完成。 此變更現已永久保存在裝置的資料庫中。 即使重新啟動應用程式後,所有項目仍將保留。
現在你已經知道如何執行獲取、更新和插入資料,那麼資料刪除呢? 我們將為 App 添加一項用於刪除待辦事項的功能。
在 ContentView
結構中,將 onDelete
修飾器附加到 ForEach
迴圈:
.onDelete(perform: { indexSet in
for index in indexSet {
let itemToDelete = todoItems[index]
modelContext.delete(itemToDelete)
}
})
這個閉包採用一個索引集來儲存要刪除的項目的索引。 若要從持久性儲存中刪除項目,只需呼叫 modelContext
的 delete
函數並指定要刪除的項目即可。
onDelete
修飾器會自動啟用清單檢視中的滑動刪除功能。 要嘗試此操作,只需運行 App 並滑動即可刪除項目。 這將從資料庫中完全刪除該項目。
若要使用虛擬資料填充資料庫,你可以設定載入範例資料的預覽容器。
@MainActor
let previewContainer: ModelContainer = {
do {
let container = try ModelContainer(for: ToDoItem.self, configurations: ModelConfiguration(isStoredInMemoryOnly: true))
for _ in 1...10 {
container.mainContext.insert(generateRandomTodoItem())
}
return container
} catch {
fatalError("Failed to create container")
}
}()
在上面的程式碼中,我們設定了一個記憶體資料庫,其中填充了 10 個隨機待辦事項。 預覽容器準備就緒後,你可以將其附加到 #Preview
區塊中的 ContentView
:
#Preview {
ContentView()
.modelContainer(previewContainer)
}
我希望你現在更了解如何將 SwiftData 整合到 SwiftUI 專案中以及如何執行所有基本 CRUD(建立、讀取、更新和刪除)操作。 Apple 付出了大量努力,讓 Swift 開發人員和新手更輕鬆地進行持久資料管理和資料建模。
雖然 Core Data 仍然是向後相容的選項,但現在是時候學習 SwiftData 框架了,特別是如果你正在開發專門針對 iOS 17 或更高版本的應用程式。 採用這個新框架來利用 SwiftData 提供的增強功能和優勢。
你可以在此處下載演示項目作為參考: