@Model
class Habit: Identifiable {
@Attribute(.unique) var id: UUID = UUID()
@Relationship(deleteRule: .cascade) var completedDays: CompletedDays?
var name: String = ""
var color: String = ""
init(completedDays: CompletedDays? = nil, name: String, color: String) {
self.completedDays = completedDays
self.name = name
self.color = color
}
}
@Model
class CompletedDays {
@Relationship(inverse: \Habit.completedDays) var habit: Habit?
var days: [Int: [Int: [Int]]] = [:]
init(habit: Habit? = nil) {
self.habit = habit
}
}
在我的应用程序中,每个习惯都有一个completedDays字典,如下所示:[年[月[日]]]。您可以点击某一天,它就会突出显示。这本词典包含所有已突出显示的日期。
我的一个朋友建议将completedDays 字典与Habit 模型分开。这样我就可以加载习惯的名称和颜色,而不必同时将所有completedDays 字典加载到内存中。
所以我尝试将其分离到它自己的模型中,称为 CompletedDays 和天字典。这样我就可以做到这一点:
@Query var listHabits: [Habit]
,我希望不会将所有completedDays 字典加载到内存中,只加载对它们的引用。
但是如果我尝试使用下面的方法初始化一个新习惯,应用程序就会崩溃。
// create new habit
let name = "test"
let color = "8cb6fa"
let habit = Habit(name: name, color: color)
let completedDays = CompletedDays(habit: habit)
print("The app should crash now...")
habit.completedDays = completedDays // <- it crashes if I add this line, no crash if I remove this line
dbContext.insert(habit)
print("If you made it here the app didn't crash")
当 XCode 崩溃时,它会在我的工作区中插入一些额外的代码,并出现以下错误:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x2355876b0)
@Model
class Habit: Identifiable {
@Attribute(.unique) var id: UUID = UUID()
// Relationship to CompletedDays, with cascade delete rule.
@Relationship(deleteRule: .cascade) var completedDays: CompletedDays?
__________________________________________________________________________
{
// Relationship to CompletedDays, with cascade delete rule.
@storageRestrictions(accesses: _$backingData, initializes: _completedDays)
init(initialValue) {
_$backingData.setValue(forKey: \.completedDays, to: initialValue)
_completedDays = _SwiftDataNoType()
}
// Relationship to CompletedDays, with cascade delete rule.
get {
_$observationRegistrar.access(self, keyPath: \.completedDays)
return self.getValue(forKey: \.completedDays)
}
// Relationship to CompletedDays, with cascade delete rule.
set {
_$observationRegistrar.withMutation(of: self, keyPath: \.completedDays) {
self.setValue(forKey: \.completedDays, to: newValue) // <- Error: Thread 1: EXC_BREAKPOINT (code=1, subcode=0x2355876b0)
}
}
}
__________________________________________________________________________
我现在尝试以多种方式设置这种关系,但我总是遇到某种与循环依赖相关的错误。我修复循环依赖的尝试是根据我在下面找到的信息,在两个模型中将 CompletedDays 和 Habit 设为可选,默认值为零。但我仍然面临上面的错误。
https://www.hackingwithswift.com/quick-start/swiftdata/how-to-create-one-to-one-relationships
我的做法是否完全错误,或者这个设置对我的目标有意义吗?
我在下面创建了一个 MRE:
import SwiftUI
import SwiftData
@main
struct TestApplication: App {
var body: some Scene {
WindowGroup {
TestView()
.modelContainer(for: [Habit.self, CompletedDays.self], inMemory: true)
}
}
}
@Model
class Habit: Identifiable {
@Attribute(.unique) var id: UUID = UUID()
@Relationship(deleteRule: .cascade) var completedDays: CompletedDays?
var name: String = ""
var color: String = ""
init(completedDays: CompletedDays? = nil, name: String, color: String) {
self.completedDays = completedDays
self.name = name
self.color = color
// I tried this as well, but I get an error even if I set completedDays to nil first
// self.completedDays = CompletedDays(habit: self)
}
}
@Model
class CompletedDays {
@Relationship(inverse: \Habit.completedDays) var habit: Habit?
var days: [Int: [Int: [Int]]] = [:]
init(habit: Habit? = nil) {
self.habit = habit
}
}
struct TestView: View {
@Environment(\.modelContext) var dbContext
@State private var habitNameInput: String = ""
var body: some View {
VStack {
Text("Tap save and the app should crash")
.font(.subheadline)
.padding()
saveButton()
}
.padding([.leading, .trailing], 40)
}
func storeHabit() {
let name = "test"
let color = "8cb6fa"
let habit = Habit(name: name, color: color)
let completedDays = CompletedDays(habit: habit)
print("The app should crash now...")
habit.completedDays = completedDays // <- it crashes if I add this line, no crash if I remove this line
dbContext.insert(habit)
print("If you made it here the app didn't crash")
}
func saveButton() -> some View {
Button {
storeHabit()
} label: {
ZStack {
RoundedRectangle(cornerRadius: 2)
.stroke(.gray, lineWidth: 1)
Text("Save")
}
.frame(width: 80, height:40)
}
}
}
我通过在定义它的completedDays 属性之前将习惯插入数据库来消除错误。我在应用程序中测试了它,它似乎有效。
我仍然不确定是否有更好的方法来实现我的目标而不使用两个单独的模型,但应用程序不再崩溃。
func storeHabit() {
let name = "test"
let color = "8cb6fa"
let habit = Habit(name: name, color: color)
dbContext.insert(habit) // 1. Insert the habit first
let completedDays = CompletedDays(habit: habit)
//dbContext.insert(completedDays) // chatGPT suggested I add this line, but after testing it seems like it's not needed.
habit.completedDays = completedDays // 2. Now we can define habit.completedDays without crashing
}