置頂
菜鳥入門筆記,如有謬誤之處還請大佬指出
深耕細作 笃行緻遠
前言
在開發IOS應用的過程中,不可避免會遇到需要存儲大量複雜資料的場景,在對比如下幾種方式後,我認為Core Data的支撐場景應該更為廣泛,随即決定先拿下它。
- • UserDefaults:一種簡單鍵值對,值為string,類似web中的cookie
- • plist:是 Property List(屬性清單)的縮寫,是一種用于存儲和序列化資料的檔案格式,它最初是由蘋果公司引入的,并廣泛用于 macOS 和 iOS 平台上的應用程式和配置檔案中
- • JSON:常見的JSON文本存儲
- • Core Data:Core Data是以面向對象的方式存儲和管理資料的架構,其主要的底層存儲方式包括 SQLite、Binary、XML、In-Memory。SQLite 作為預設的底層存儲方式,無需編寫SQL語句,支援事務和多種資料類型的存儲
在我的了解來看,Core Data就是一種ORM架構
建立模型檔案
- 建立項目時勾選Core Data,初始化模闆檔案中會預設建立一個模型檔案和調用Core Data的示例,并完成了基本的CRUD操作
- 在現有項目中通過建立檔案手動建立
填寫完模型檔案名後,模型檔案将出現在目錄樹中
鑒于以學習為目的,我們采用第二種手動建立的方式
實體與屬性
當模型檔案建立好之後,下一步則是建立模型的實體與屬性,在這裡先給出幾個重要的概念:
- • 實體(Entity):對應的是資料庫中的表
- • 屬性(Attribute):對應的是資料庫中的字段
- • 關聯關系(Relationship):關聯關系與資料庫中也是一緻的,通過外鍵來引用對方的資料,在本文中不會使用到關聯關系,不做過多贅述
按照下圖完成實體與屬性的添加
通路模型
先不考慮封裝,我們的目的是做一個最小化實作并了解Core Data的大緻調用流程
在入口檔案處編寫如下代碼(初始化時入口檔案名一般為[項目名稱+App]如 BestBeforeApp.swift,如果不知道入口檔案請全局搜尋 @main):
import SwiftUI
import CoreData
@main
struct BestBeforeApp: App {
// 容器
let container: NSPersistentContainer
init(){
// 指定模型檔案
container = NSPersistentContainer(name: "BestBefore")
// 加載存儲類型
container.loadPersistentStores { description, error in
if let error = error {
fatalError("Unable to load persistent stores: \(error)")
}
}
}
var body: some Scene {
WindowGroup {
// 将環境變量賦給 ContentView 視圖,即 ContentView.swift 檔案
ContentView().environment(\.managedObjectContext, container.viewContext)
}
}
}
NSPersistentContainer類用于管理模型、内容上下文和存儲方式,貼一張官方的示意圖
代碼執行邏輯:
- 1. 使用NSPersistentContainer(name: "BestBefore")傳入模型檔案名稱給容器,拿到管理句柄
- 2. 調用容器的 container.loadPersistentStores 方法去初始化存儲方式,這裡我們沒有給出參數Core Data會預設使用SQLite作為底層的存儲器
- 3. 将環境變量managedObjectContext賦給 ContentView 視圖,即 ContentView.swift 檔案
如果通路失敗,上述代碼會抛出錯誤,一般情況下可能是模型檔案名填寫錯誤
新增資料
在 ContentView.swift 檔案中編寫代碼:
import SwiftUI
struct CoreDataView: View {
// 擷取 BestBeforeApp 傳入的環境變量并指派給 viewContext
@Environment(\.managedObjectContext) var viewContext
var body: some View{
Button {
addItem()
} label: {
Text("新增")
}
}
// 新增資料
func addItem(){
// 執行個體化一條 Item 實體的資料
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
do {
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
在 let newItem = Item(context: viewContext)中的 Item 結構體是我們在建立模型實體後,Xcode自動為我們生成的全局結構體,在項目中任何位置無需導入,可以直接使用viewContext 則是内容上下文管理器,用來管理資料的增删改查,是底層存儲方式的上層封裝
代碼執行邏輯:
- 1. 我們執行個體化了 Item 結構體,并傳入 viewContext,這麼做的目的是通知viewContext将目前執行個體存入其緩存
- 2. 然後通過 viewContext.save() 将緩存中的資料儲存到資料庫,這樣的解耦意味着我們可以在save之前做多次實體資料的操作,并一次性完成儲存,而在儲存之前資料都存儲在緩存中,并沒有真正入庫
檢視資料
這一步我們将剛才添加的資料展示到視圖中
import SwiftUI
struct CoreDataView: View {
// 實體 Item 的資料
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
var items: FetchedResults<Item>
var body: some View{
// 循環渲染資料
ForEach(items){item in
Text(item.timestamp!,style: .date)
}
Spacer()
Button {
addItem()
} label: {
Text("新增")
}
}
...
}
代碼執行邏輯:
- 1. 我們定義了一個變量 items 來存儲查詢結果
- 2. 給items添加了一個包裝器 @FetchRequest來擷取資料,并且被包裝器包裹的屬性會自動實作雙向綁定(類似于Vue的 v-model),當資料發生變化會自動觸發視圖重繪,包裝器的sortDescriptors參數則允許我們按照多個屬性進行升序或降序的排序,當然我猜應該也支援一些聚合函數
- 3. 當拿到資料後在 body 中使用ForEach周遊展示
編輯資料
編輯資料與新增資料是相同的邏輯,這裡我就偷個懶,隻貼一個代碼片段
// 編輯資料
func editItem(item: Item){
// 更新目前資料的時間戳
item.timestamp = Date()
do {
// 注意這裡同樣需要儲存入庫
try viewContext.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
- 1. 這裡我們定義了一個 editItem 方法,支援傳入一個Item執行個體去修改其屬性,當然這裡我們也可以通過 @Binding 包裝器将屬性綁定到表單控件,這種情況下則不需要傳入Item執行個體,直接調用viewContext.save() 即可
删除資料
// 删除資料
func deleteItem(index: Int){
if let item = items.indices.contains(index) ? items[index] : nil{
viewContext.delete(item)
do{
try viewContext.save()
}catch{
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
代碼執行邏輯:
- 1. 我們通過給deleteItem傳入一個 items 中的索引,去找到對應資料對象(即Item的執行個體)
- 2. 将查找到的執行個體直接傳入 viewContext.delete 方法中進行删除
- 3. 删除資料庫中的資料 viewContext.save()
總結
- 1. 我們了解到Core Data是一個抽象層類ORM架構,它不直接操作底層,其底層存儲方式可以有多種,預設的是SQLite
- 2. 對 Core Data 的 NSPersistentContainer與 managedObjectContext 有了初步的了解
- 3. 通過CRUD操作,跑通了基本的資料增删改查流程,了解到 Core Data執行 save 方法前,所有的操作都是在緩存中進行,并沒有真正入庫
别錯過精彩内容,快來關注我們的微信公衆号【尋桃】!