I am trying to make a temporary copy of a Swift Data model(Routine), but I get this breakpoint error at get of one of its propery(Routine.worksets). I am assuming this error happens of the relationship of Model, or because of how the models are used.
This is what the models look like
Routine:
A routine containes a list of workset, which contains a list of workoutset
@Model
class Routine {
var name: String
var target: [Target]
var worksets: [Workset]
// I get the error inside the get of worksets
// Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1cc1698ec)
var latestDate: Date
init(name: String, target: [Target], worksets: [Workset], latestDate: Date) {
self.name = name
self.target = target
self.worksets = worksets
self.latestDate = latestDate
}
@Model
class Workset {
@Relationship(deleteRule: .cascade, inverse: Workout.workset)
var workout: Workout
var workoutSets: [WorkoutSet]
init(workout: Workout, workoutSets: [WorkoutSet]) {
self.workout = workout
self.workoutSets = workoutSets
}
@Model
class WorkoutSet {
var weight: Double
var reps: Int
var isDone: Bool
init(weight: Double, reps: Int, isDone: Bool) {
self.weight = weight
self.reps = reps
self.isDone = isDone
}
static func isDoneChange ( lhs: WorkoutSet, rhs: WorkoutSet) -> Bool {
lhs.isDone == rhs.isDone
}
}
}
}
extension Routine {
func addWorkset(workout: Workout, context: ModelContext) {
let newWorkset = Workset(workout: workout, workoutSets: [])
context.insert(newWorkset)
self.worksets.append(newWorkset)
}
func deepCopy() -> Routine {
let copiedWorksets = self.worksets.map { workset in
let copiedWorkout = Workout(name: workset.workout.name, target: workset.workout.target, tool: workset.workout.tool, maxWeight: workset.workout.maxWeight, isBookmarked: workset.workout.isBookmarked)
let copiedWorkoutSets = workset.workoutSets.map { workoutSet in
Workset.WorkoutSet(weight: workoutSet.weight, reps: workoutSet.reps, isDone: workoutSet.isDone)
}
return Workset(workout: copiedWorkout, workoutSets: copiedWorkoutSets)
}
return Routine(name: self.name, target: self.target, worksets: copiedWorksets, latestDate: self.latestDate)
}
}
Workout:
@Model
class Workout: Identifiable,Equatable, Hashable{
var workset: Routine.Workset?
let id: UUID
var name: String
var target: [Target]
var tool: WorkTool
var maxWeight: Double
var isBookmarked: Bool
init(id: UUID = UUID(), name: String, target: [Target], tool: WorkTool, maxWeight: Double, isBookmarked: Bool) {
self.id = id
self.name = name
self.target = target
self.tool = tool
self.maxWeight = maxWeight
self.isBookmarked = isBookmarked
}
static func == (lhs: Workout, rhs: Workout) -> Bool {
return lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
workTool and Target:
enum WorkTool: String, CaseIterable, Codable {
case barbell
case dumbbell
case machine
case bodyWeight
var stringValue: String {
switch self {
case .barbell:
return "Barbell"
case .dumbbell:
return "Dumbbell"
case .machine:
return "Machine"
case .bodyWeight:
return "Body Weight"
}
}
}
enum Target: String, CaseIterable, Codable {
case chest
case back
case legs
case shoulders
case biceps
case triceps
case abs
static func stringValues(targets: [Target]) -> String {
return targets.map { $0.rawValue }.joined(separator: ", ")
}
}
And this is how I use the Routine Model:
The views are stacked as
Main -> RoutineDetail -> WorkingOutView
I cutted out only the parts that are related because this codes work fine.
Main
gets the list of routines
@Query private var routines: [Routine]
@State var selectedRoutine: Routine = Routine(name: "", target: [], worksets: [], latestDate: Date())
ForEach(routines){ routine in
.onTapGesture {
selectedRoutine = routine
// showSheet = true
.sheet(isPresented: $showSheet, content: {
RoutineDetailView(routine: selectedRoutine, path: $path, isClicked: $showSheet, isWorking: $isWorking)
})
RoutineDetail
view shows the details of selected routine, and it is where I call the copy function to create a temporary copy for a routine
struct RoutineDetailView: View {
var routine: Routine
@State var tempRoutine: Routine = Routine(name: "", target: [], worksets: [], latestDate: Date())
Button(action: {
tempRoutine = routine.deepCopy()
WorkingOutView
contains texfields and buttons that allow users record their workout status which updates the routine.
the reason I am trying to make a temporary copy of a routine is because when users make changes at WorkingOutView
, the original routine has to kept separatly so that a user can decide whether to overwrite it or not.
To be more specific, to start working out, a user selects a routine to start with. When working out, users might want to add or delete workout
and the workoutsets
depending on their daily routine. However, when they are done working out, they should be able to choose whether if they want to overwrite the original routine with the new changes they have made throughout their workout, or keep the routine as it was.
I am assuming the error is occured by the design of the models and I would appreciate on advice how I should design these models.