I have an issue about insert a many to many relationship with SwiftData, the insert work well in this case but the upsert crash the app with this error EXC_BAD_ACCESS
I think my big question is "How to insert a many to many relationship object without problem?"
I give a schema of the database relationship !
class GoalsReadUseCase: GoalsReadUseCaseProtocol {
var modelContext: ModelContext
var repo: GoalRepository
init(modelContext: ModelContext, repo: GoalRepository) {
self.modelContext = modelContext
self.repo = repo
}
func execute(isForced: Bool = false) async -> Result<[Goal], GoalsReadUseCaseError> {
do {
let response = try await repo.getGoals()
response.forEach { goalResponse in
let goal = Goal(
id: goalResponse.id,
frequency: goalResponse.frequency,
sentence: nil,
timezone: goalResponse.timezone,
motivationContents: [],
tasks: [],
milestones: [],
notificationAt: goalResponse.notificationAt,
createdAt: goalResponse.createdAt,
updatedAt: goalResponse.updatedAt
)
modelContext.insert(goal)
let prefix = Prefix(
id: goalResponse.sentence.prefix.id,
text: goalResponse.sentence.prefix.text,
hint: goalResponse.sentence.prefix.hint,
language: goalResponse.sentence.prefix.language
)
_ = Sentence(
id: goalResponse.sentence.id,
body: goalResponse.sentence.body,
endAt: goalResponse.sentence.endAt,
prefix: prefix,
goal: goal
)
goalResponse.motivationContents.forEach { motivationContentResponse in
let motivationContent = MotivationContent(
id: motivationContentResponse.id,
title: motivationContentResponse.title,
body: motivationContentResponse.body,
createdAt: motivationContentResponse.createdAt,
updatedAt: motivationContentResponse.updatedAt,
goal: goal
)
goal.motivationContents.append(motivationContent)
}
goalResponse.milestones.forEach { milestoneResponse in
let milestone = Milestone(
id: milestoneResponse.id,
text: milestoneResponse.text,
goalId: milestoneResponse.goalId,
isFinish: milestoneResponse.isFinish,
createdAt: milestoneResponse.createdAt,
updatedAt: milestoneResponse.updatedAt,
goal: goal
)
goal.milestones.append(milestone)
}
goalResponse.tasks.forEach { taskResponse in
let dailyTask = DailyTask(
id: taskResponse.id,
text: taskResponse.text,
goalId: taskResponse.goalId,
isFinish: taskResponse.isFinish,
createdAt: taskResponse.createdAt,
updatedAt: taskResponse.updatedAt,
goal: goal
)
goal.tasks.append(dailyTask)
}
}
if modelContext.hasChanges {
try modelContext.save()
}
let fetchMotivationContent = FetchDescriptor<Goal>()
let result = try modelContext.fetch(fetchMotivationContent)
return .success(result)
} catch let error {
debugPrint(error)
switch(error){
case APIError.decode:
return .failure(.decodingError)
default:
return .failure(.networkError)
}
}
}
}
class Goal {
// *********************************************************************
// MARK: - Properties
@Attribute(.unique) var id: UUID
var frequency: String
var timezone: String
var notificationAt: Date
@Relationship(deleteRule: .cascade, inverse: \DailyTask.goal) var tasks: [DailyTask] = []
@Relationship(deleteRule: .cascade, inverse: \Milestone.goal) var milestones: [Milestone] = []
@Relationship(deleteRule: .cascade, inverse: \MotivationContent.goal) var motivationContents: [MotivationContent] = []
@Relationship(deleteRule: .cascade, inverse: \Sentence.goal) var sentence: Sentence?
var createdAt: Date
var updatedAt: Date
}
class DailyTask: TodoTask {
@Attribute(.unique) var id: UUID
var text: String
var goalId: UUID
var isFinish: Bool
var createdAt: Date
var updatedAt: Date
var goal: Goal?
}
class Milestone: TodoTask {
@Attribute(.unique) var id: UUID
var text: String
var goalId: UUID
var isFinish: Bool
var createdAt: Date
var updatedAt: Date
var goal: Goal?
}```
```@Model
class MotivationContent: MotivationContentProtocol {
// *********************************************************************
// MARK: - Properties
@Attribute(.unique) var id: UUID
var title: String
var body: String
var createdAt: Date
var updatedAt: Date
var goal: Goal?
}
class Sentence {
// *********************************************************************
// MARK: - Properties
@Attribute(.unique) var id: UUID
var body: String
var endAt: Date
@Relationship(inverse: \Prefix.sentences)
var prefix: Prefix?
var goal: Goal?
}
class Prefix {
@Attribute(.unique) var id: UUID
var text: String
var hint: String
var language: String
var sentences: [Sentence] = []
}
I tried to change the construction of the Goal object in different way to insert in database