Search code examples
iosswiftcore-dataswiftui

swift core data how should I save and fetch the one to many relationship and assign value to my viewModel entity


im using UIKit delegate to manage app lifecycle but using swiftUI do the UI stuff.

lets say I have following data model entities, and i want to cached it via core data.

class ClassEntity: Codable {
    var classTitleName: String
    var classRank: Int
    var students: [StudentEntity]
    var teachers: [TeacherEntity]
    
    init (classTitleName: String,
          classRank: Int,
          students: [StudentEntity],
          teachers: [TeacherEntity]) {
        self.classTitleName = classTitleName
        self.classRank = classRank
        self.students = students
        self.teachers = teachers
    }
    
}


class StudentEntity: Codable {
    var studentName: String
    var studentGender: String
    
    init(studentName: String,
         studentGender: String){
        self.studentName = studentName
        self.studentGender = studentGender
    }
}

class TeacherEntity: Codable{
    var teacherName: String
    var teacherGender: String
    
    init(teacherName: String,
         teacherGender: String){
        self.teacherName = teacherName
        self.teacherGender = teacherGender
    }
}

so base on above entities, i created core data entity like following: enter image description here enter image description here enter image description here

Then, lets say i have two viewModel called SaveClassInfoViewModel FetchCachedClassInfoViewModel, first one do save and second one do fetch

so the ViewModel will be like :


class SaveClassInfoViewModel: ObservableObject {
    var schoolTotalClass: [ClassEntity]
    init(schoolTotalClass: [ClassEntity]) {
        self.schoolTotalClass = schoolTotalClass // data will be inject from previous VC , lets say we have data there.
    }
    
    func saveSchoolTotalClassInfo() {
        guard let appDelegate =
            UIApplication.shared.delegate as? AppDelegate else {
            return
          }
        
        let managedContext =
            appDelegate.persistentContainer.viewContext
        
        // how should i save data to cache ?
    }
}
class FetchCachedClassInfoViewModel: ObservableObject {
    
    @Published var schoolTotalClass: [ClassEntity] = []
    
    func fetchData() {
        guard let appDelegate =
            UIApplication.shared.delegate as? AppDelegate else {
            return
          }
        
        let managedContext =
            appDelegate.persistentContainer.viewContext
        
        // how should i do fetch so that im able to assign all the stored data to `schoolTotalClass` ?
        // if able to assign cached data to @Published wrapped schoolTotalClass?
        // once i got data , i can consume `@Published var schoolTotalClass` in view file directly
    }
}

how should i do save and fetch there, is my core data entity definition correct ? thanks very much


Solution

  • Rather creating relation like this you can do it saving the class id in teacher and student and get them on the basis of id like as:

    class ClassEntity {
        let id:String
        var classTitleName: String
        var classRank: Int
    
        
        init (id:String,
              classTitleName: String,
              classRank: Int) {
            self.id = id
            self.classTitleName = classTitleName
            self.classRank = classRank
        }
        
    }
    

    then your teacher and student

    class StudentEntity {
        var classId:String
        var studentName: String
        var studentGender: String
        
        init(classId:String,studentName: String,
             studentGender: String){
            self.classId = classId
            self.studentName = studentName
            self.studentGender = studentGender
        }
    }
    
    class TeacherEntity {
        var classId:String
        var teacherName: String
        var teacherGender: String
        
        init(classId:String,teacherName: String,
             teacherGender: String){
            self.classId = classId
            self.teacherName = teacherName
            self.teacherGender = teacherGender
        }
    }
    

    then create coredatastack

    import Foundation import CoreData

    class CoreDataStack: NSObject {
       
        private let modelName: String
        lazy var managedContext: NSManagedObjectContext = {
            return self.storeContainer.viewContext
        }()
        
        init(modelName: String) {
            self.modelName = modelName
        }
        
        private lazy var storeContainer: NSPersistentContainer = {
           
            let container = NSPersistentContainer(name: self.modelName)
            container.loadPersistentStores { (storeDescription, error) in
                if let error = error as NSError? {
                    print("Unresolved error \(error), \(error.userInfo)")
                }
            }
            container.viewContext.automaticallyMergesChangesFromParent = true
            container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
            return container
        }()
        
        
        func saveContext () {
            guard managedContext.hasChanges else { return }
            do {
                try managedContext.save()
            } catch let error as NSError {
                print("Unresolved error \(error), \(error.userInfo)")
            }
        }
        
        func updateContext() {
            do {
                try managedContext.save()
            } catch let error as NSError {
                print("Unresolved error \(error), \(error.userInfo)")
            }
        }
        
        func clearChanges() {
            managedContext.rollback()
        }
        
        func deleteReminder(reminder:Reminder) {
            managedContext.delete(reminder)
            updateContext()
        }
        
        func deleteNote(note:Note) {
            managedContext.delete(note)
            updateContext()
        }
        
        func deleteFromIds(selectedId:SubCategoryIds) {
            managedContext.delete(selectedId)
            updateContext()
        }
        
    }
    

    then create a manager

    import Foundation
    import CoreData
    
    class CoreDataManager {
        
        static let shared = CoreDataManager()
        private init(){}
        lazy var coreDataStack = CoreDataStack(modelName: "YourModelName")
        
        func allClasses() -> [ClassEntity] {
            let fetechRequest: NSFetchRequest<ClassEntity> = ClassEntity.fetchRequest()
            do {
                let results = try coreDataStack.managedContext.fetch(fetechRequest)
                return results
            } catch {
                
            }
            return [ClassEntity]()
        }
        
        func allStudentOfClass(classId:String) -> [StudentEntity] {
            let fetechRequest: NSFetchRequest< StudentEntity > = StudentEntity.fetchRequest()
            fetechRequest.predicate = NSPredicate(format: "classId == %@",
                                                  argumentArray: [classId])
            do {
                let results = try coreDataStack.managedContext.fetch(fetechRequest)
                return results
            } catch {
                
            }
            return [StudentEntity]()
        }
        
          func allTeacherOfClass(classId:String) -> [TeacherEntity] {
             let fetechRequest: NSFetchRequest<TeacherEntity> = TeacherEntity.fetchRequest()
           fetechRequest.predicate = NSPredicate(format: "classId == %@",
                                              argumentArray: [classId])
            do {
              let results = try coreDataStack.managedContext.fetch(fetechRequest)
              return results
           } catch {
    
        }
        return [TeacherEntity]()
      }
      
        
    }
        
       
    

    how you can create a class

     func createClass() {
        let id = "Class 1" //or whatever you want to say here
        let _class = Class(context: CoreDataManger.shared.coreDataStack.managedContext)
        _class.id = id
        _class.classTitleName = "Title"
        _class.classRank = 12
        CoreDataManger.shared.coreDataStack.saveContext()
    }
    

    same as how create teacher with this class

     func addTeacher(classId:String) {
            let _class = TeacherEntity(context: CoreDataManger.shared.coreDataStack.managedContext)
            _class.classId = classId
            _class.teacherGender = "Male"
            _class.teacherName = "Alex"
            CoreDataManger.shared.coreDataStack.saveContext()
        }