import Foundation import SwiftData struct DataLoader { static let logger = AppLogger( subsystem: Bundle.main.bundleIdentifier ?? "dev.rzen.indie.Workouts", category: "InitialData" ) // Data structures for JSON decoding private struct ExerciseTypeData: Codable { let name: String let descr: String } private struct MuscleGroupData: Codable { let name: String let descr: String } private struct MuscleData: Codable { let name: String let descr: String let muscleGroup: String } private struct ExerciseData: Codable { let name: String let setup: String let descr: String let sets: Int let reps: Int let weight: Int let type: String let muscles: [String] } private struct SplitExerciseAssignmentData: Codable { let exercise: String let weight: Int let sets: Int let reps: Int } private struct SplitData: Codable { let name: String let intro: String let splitExerciseAssignments: [SplitExerciseAssignmentData] } @MainActor static func create(modelContext: ModelContext) { logger.info("Creating initial data from JSON files") // Load and insert data do { // Dictionaries to store references var exerciseTypes: [String: ExerciseType] = [:] var muscleGroups: [String: MuscleGroup] = [:] var muscles: [String: Muscle] = [:] var exercises: [String: Exercise] = [:] // 1. Load Exercise Types let exerciseTypeData = try loadJSON(forResource: "exercise-types", type: [ExerciseTypeData].self) for typeData in exerciseTypeData { let exerciseType = ExerciseType(name: typeData.name, descr: typeData.descr) exerciseTypes[typeData.name] = exerciseType modelContext.insert(exerciseType) } // 2. Load Muscle Groups let muscleGroupData = try loadJSON(forResource: "muscle-groups", type: [MuscleGroupData].self) for groupData in muscleGroupData { let muscleGroup = MuscleGroup(name: groupData.name, descr: groupData.descr) muscleGroups[groupData.name] = muscleGroup modelContext.insert(muscleGroup) } // 3. Load Muscles let muscleData = try loadJSON(forResource: "muscles", type: [MuscleData].self) for data in muscleData { // Find the muscle group for this muscle if let muscleGroup = muscleGroups[data.muscleGroup] { let muscle = Muscle(name: data.name, descr: data.descr, muscleGroup: muscleGroup) muscles[data.name] = muscle modelContext.insert(muscle) } else { logger.warning("Muscle group not found for muscle: \(data.name)") } } // 4. Load Exercises let exerciseData = try loadJSON(forResource: "exercises", type: [ExerciseData].self) for data in exerciseData { let exercise = Exercise(name: data.name, setup: data.setup, descr: data.descr, sets: data.sets, reps: data.reps, weight: data.weight) // Set exercise type if let type = exerciseTypes[data.type] { exercise.type = type } else { logger.warning("Exercise type not found: \(data.type) for exercise: \(data.name)") } // Set muscles var exerciseMuscles: [Muscle] = [] for muscleName in data.muscles { if let muscle = muscles[muscleName] { exerciseMuscles.append(muscle) } else { logger.warning("Muscle not found: \(muscleName) for exercise: \(data.name)") } } exercise.muscles = exerciseMuscles exercises[data.name] = exercise modelContext.insert(exercise) } // 5. Load Splits and Exercise Assignments let splitData = try loadJSON(forResource: "splits", type: [SplitData].self) for data in splitData { let split = Split(name: data.name, intro: data.intro) modelContext.insert(split) // Create exercise assignments for this split for (index, assignment) in data.splitExerciseAssignments.enumerated() { if let exercise = exercises[assignment.exercise] { let splitAssignment = SplitExerciseAssignment( order: index + 1, // 1-based ordering sets: assignment.sets, reps: assignment.reps, weight: assignment.weight, split: split, exercise: exercise ) modelContext.insert(splitAssignment) } else { logger.warning("Exercise not found: \(assignment.exercise) for split: \(data.name)") } } } // Save all the inserted data try modelContext.save() logger.info("Initial data loaded successfully from JSON files") } catch { logger.error("Failed to load initial data from JSON files: \(error.localizedDescription)") } } // Helper method to load and decode JSON from a file private static func loadJSON(forResource name: String, type: T.Type) throws -> T { guard let url = Bundle.main.url(forResource: name, withExtension: "json") else { logger.error("Could not find JSON file: \(name).json") throw NSError(domain: "InitialData", code: 1, userInfo: [NSLocalizedDescriptionKey: "Could not find JSON file: \(name).json"]) } do { let data = try Data(contentsOf: url) let decoder = JSONDecoder() return try decoder.decode(T.self, from: data) } catch { logger.error("Failed to decode JSON file \(name).json: \(error.localizedDescription)") throw error } } }