SwiftData Crash during the upsert

93 views Asked by At

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 !

Schema of DB

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

0

There are 0 answers