john smith
john smith

Reputation: 105

How to Duplicate a SwiftData object

I have a SwiftData model called Workout that contains a array of Exercise Model which itself contains an array of Sets Model,

Before I had a copy method in these models for creating a new object from the properties of old objects and it worked correctly, but since introducing Relationships between these models whenever I try to call the copy method of Exercise I get the error message:

Fatal error: Unsupported relationship key path ReferenceWritableKeyPath<MySet, Exercise>

This are the copy methods:

@Model
class Workout: Identifiable {
    var id: UUID
    var name: String
    @Relationship(deleteRule: .cascade, inverse: \Exercise.workout) //when deleting a workout you delete all exericses that happened during that workout
    var exercises: [Exercise]?
    var startTime: Date
    var endTime: Date
    
    func copy() -> Workout {
        let workout = Workout(id: UUID(), name: name, startTime: startTime, endTime: endTime)
          workout.name = name
          workout.startTime = Date.now
          workout.endTime = Date.now
          workout.exercises = exercises?.map { $0.copy() }
          return workout
      }
    
    init(id: UUID, name: String = "", startTime: Date = Date.now, endTime: Date = Date.now) {
        self.id = id
        self.name = name
        self.startTime = startTime
        self.endTime = endTime
    }
    
    

}



@Model
class Exercise: Identifiable {
    var id: UUID
    var exerciseName: ExerciseName
    
    @Relationship(deleteRule: .cascade, inverse: \MySet.exercise)
    var sets: [MySet]?    //When an Exercise gets Deleted all Sets associated need to get deleted

    var date: Date
    var workout: Workout
 
    func copy() -> Exercise {
        let newExercise = Exercise(id: UUID(), exerciseName: exerciseName, date: Date.now, workout: workout)
        
        // Copy the related sets
        if let existingSets = sets {
            newExercise.sets = existingSets.map { set in
                // Copy each set and associate it with the newExercise
                let newSet = set.copy(exercise: newExercise)
                return newSet
            }
        }
        
        return newExercise
    }
    
    init(id: UUID, exerciseName: ExerciseName, date: Date, workout: Workout) {
        self.id = id
        self.exerciseName = exerciseName
        self.date = date
        self.workout = workout
    }

    
}


@Model
class MySet: Identifiable {
    var id: UUID
    var weight: Int
    var reps: Int
    var isCompleted: Bool
    
 
    var exercise: Exercise
    
    var date: Date
    
    //make a copy functionality so you can call this and pass it an Exercise when copying a Set
    func copy(exercise: Exercise) -> MySet {
        MySet(id: UUID(), weight: weight, reps: reps, isCompleted: isCompleted,date: Date.now , exercise: exercise)
        }
    
    init(
        id: UUID,
        weight: Int,
        reps: Int,
        isCompleted: Bool,
        date: Date,
        exercise: Exercise
        
    ) {
        self.id = id
        self.weight = weight
        self.reps = reps
        self.isCompleted = isCompleted
        self.date = date
        self.exercise = exercise
      
    }
    

    
}

Xcode says the error occurs in the set method of var sets: [MySet]?

why did these copy methods work before introducing an inverse relationships between these models and not now?

Upvotes: 0

Views: 113

Answers (1)

GoksuBayy
GoksuBayy

Reputation: 84

According to this answer here, you cannot set a relationship without using modelContext. So we need two things to do

  1. Using ModelContext
  2. Creating new entities for this copy function

Here is the code example (I hope it works):

func copy() -> Exercise {
let newExercise = Exercise(id: UUID(), exerciseName: exerciseName, date: Date(), workout: workout)
context.insert(newExercise)
    // Copy each set and associate it with the new exercise
if let _sets = sets {
    newExercise.sets = _sets.map { set in
        let newSet = MySet(id: UUID(), weight: set.weight, reps: set.reps, isCompleted: set.isCompleted, date: Date.now, exercise: newExercise)
        
        // Insert the newSet into the model context
        context.insert(newSet)
        
        return newSet
    }
}

return newExercise
}

Upvotes: 0

Related Questions