我在更新 SwiftData 模型上的 Bool 时遇到问题,一开始它正在改变。但是,如果我最小化/关闭应用程序并再次重新打开,则会添加返回到第一次记录的值(错误)
import SwiftUI
import SwiftData
struct CategoryList: View {
@Environment(\.modelContext) private var context
@Query var categories: [Category]
@State private var isFormSheetPresented = false // State for presenting the Add Category form
var body: some View {
List {
// Active Categories
Section(header: Text("Active Categories")
.font(.headline)
.foregroundColor(.black)) { // Header with black text
ForEach(activeCategories) { category in
HStack {
Text(category.name)
.foregroundColor(.darkSpringGreen)
Spacer()
if !category.isDefault {
// Show a delete button for non-default categories
Image(systemName: "trash")
.foregroundColor(.red)
.onTapGesture(perform: {
deleteReactivateCategory(category, isDelete: true)
})
}
}
}
}
.listRowBackground(Color.white) // Set white background for the section
// Deleted Categories
Section(header: Text("Deleted Categories")
.font(.headline)
.foregroundColor(.black)) { // Header with black text
ForEach(deletedCategories) { category in
HStack {
Text(category.name)
.foregroundColor(.lightSilver) // Indicate that it's deleted
Spacer()
Image(systemName: "arrow.trianglehead.counterclockwise.rotate.90")
.foregroundColor(.red)
.onTapGesture(perform: {
deleteReactivateCategory(category, isDelete: false)
})
}
}
}
.listRowBackground(Color.white) // Set white background for the section
}
.listStyle(PlainListStyle()) // Use plain list style to remove default styling
.navigationTitle("Categories") // Title is centered by default
.navigationBarTitleDisplayMode(.inline) // Make title not large
.navigationBarItems(trailing: Button(action: { isFormSheetPresented = true // Present the form
}) {
Text("Add")
})
.sheet(isPresented: $isFormSheetPresented) {
CategoryForm(isEdit: false, dismissAction: {
isFormSheetPresented = false // Dismiss action for the form
}, addAction: { newCategoryName in
let newCategory = Category(name: newCategoryName, isDefault: false, isDeleted: false)
let swifDataService = SwiftDataService(context: context)
do {
try swifDataService.createCategory(newCategory: newCategory)
isFormSheetPresented = false
} catch {
print("-- Cateogy failed to add \(newCategoryName)")
}
}).presentationDetents([.medium])
}
}
// Computed properties to separate active and deleted categories
private var activeCategories: [Category] {
categories
.filter { !$0.isDeleted } // Filter out deleted categories
.sorted { (first, second) -> Bool in
// Check if either category is named "Others"
if first.name == "Others" {
return false // "Others" should come last
}
if second.name == "Others" {
return true // "Others" should come last
}
// Otherwise, sort alphabetically
return first.name < second.name
}
}
private var deletedCategories: [Category] {
categories.filter { $0.isDeleted }
}
private func deleteReactivateCategory(_ categoryRecord: Category, isDelete: Bool) {
let updateCategories = categories
if let index = updateCategories.firstIndex(where: { $0.id == categoryRecord.id }) {
updateCategories[index].isDeleted = true
updateCategories[index].name = "Test Changing the name"
}
}
}
这是模型
import Foundation
import FirebaseFirestore
import SwiftData
@Model
class Category: Codable, Identifiable {
@Attribute(.unique) var id: String
var name: String
var isDefault: Bool
var isDeleted: Bool
// Custom CodingKeys to map property names for encoding/decoding
enum CodingKeys: String, CodingKey {
case id, name, isDefault, isDeleted
}
// Required initializer to decode data
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
isDefault = try container.decode(Bool.self, forKey: .isDefault)
isDeleted = try container.decode(Bool.self, forKey: .isDeleted)
}
// Encode function to encode data
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(name, forKey: .name)
try container.encode(isDefault, forKey: .isDefault)
try container.encode(isDeleted, forKey: .isDeleted)
}
// Initializer for creating new instances of Category
init(id: UUID = UUID(), name: String, isDefault: Bool, isDeleted: Bool) {
self.id = id.uuidString
self.name = name
self.isDefault = isDefault
self.isDeleted = isDeleted
}
}
让我困惑的是,为什么当我更改字符串时,即使我最小化或关闭应用程序,它仍然持久并保存,但不是 isDelete?已经5个小时了我试图找到我做错的地方但仍然没有找到它T_T...
您遇到的问题似乎是因为对 isDeleted 的更改没有保存到您的持久存储中。在 SwiftData 中,为了确保即使在应用程序关闭后更改仍然存在,您需要在修改模型后显式保存上下文。
在您的deleteReactivateCategory函数中,更新isDeleted属性后,添加一个调用来保存上下文:
private func deleteReactivateCategory(_ categoryRecord: Category, isDelete: Bool) {
if let index = categories.firstIndex(where: { $0.id == categoryRecord.id }) {
categories[index].isDeleted = isDelete
do {
try context.save()
} catch {
print("Failed to save context: \(error)")
}
}
}