NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

A question about MVVM and CoreData

Forums > SwiftUI

Hello everyone. I have a question related to MVVM and CoreData. I previously called/updated coredata entities inside Views, but soon realised that the code was getting messy. So I decided to use MVVM to implement a clean architecture. After watching a couple of tutorials, I made a sample model for one of my entities. I am not sure whether I am doing this in the right way or not. The code sample works perfectly. I used this as a Model and I used a ModelView of the View to call this. May you be able to check this code and tell me what can be improved, please? The code is self-explanatory, but if you need more information, please ask. (I am new to Swift)

Also, if the entity has a large amount of data, calling the read function within the Init() is not efficient. In that case, I assume the best thing to do is the use of NSPredicate along with the relevant parameters to the project.

import Foundation
import CoreData

class DogsModel: ObservableObject {

    static let dogsModel = DogsModel()

    @Published var dogs: [Dogs] = []
    let context = PersistenceController.shared.container.viewContext

    init() {
        readRecords()
    }

    func readRecords() {
        let request = NSFetchRequest<Dogs>(entityName: "Dogs")
        DispatchQueue.main.async {
            do {
                self.dogs = try self.context.fetch(request)
            }
            catch let error {
                print("Error fetching results \(error)")
            }
        }
    }

    func writeRecords(name:String, age:Int, breed:String) {
        let newDog = Dogs(context: context)
        newDog.uuid = UUID()
        newDog.name = name
        newDog.age = age
        newDog.breed = breed
        PersistenceController.shared.save()
        self.readRecords()
    }

    func updateRecords(record: Dogs, name:String, age:Int, breed:String) {
        record.name = name
        record.age = age
        record.breed = breed
        PersistenceController.shared.save()
        self.readRecords()
    }

    func deleteRecords(record: Dogs) {
        PersistenceController.shared.delete(record)
        self.readRecords()
    }
}

   

hi,

apart from some naming issues (e.g., a Dogs object in Core Data represents a single dog, and writeRecords simply adds one new dog to the database), you've got the bones of what you're doing pretty much right.

my suggestions would be a slight rewrite of write, update, and delete, plus removing the need to keep re-reading the data from Core Data, would be

    func addNewDog(name: String, age: Int, breed: String) {
        let newDog = Dogs(context: context)
        newDog.uuid = UUID()
        newDog.name = name
        newDog.age = age
        newDog.breed = breed
        PersistenceController.shared.save()
        //self.readRecords() ... there's no need to re-read the entire array here
        dogs.append(newDog)  // just add this to your view model's array
    }

    func update(dog: Dogs, name: String, age: Int, breed: String) {
        // first, you will be changing some element of the dogs array, so this 
        // will tell any observer that the content of the @Published dogs array 
        // has changed.  this is required, because changing properties of one of the dogs in the
        // array does not change the array of dogs, which contains class object references
        objectWillChange.send() 
        dog.name = name
        dog.age = age
        dog.breed = breed
        PersistenceController.shared.save()
        //self.readRecords() ... there's no need to re-read the entire array here
    }

    func delete(dog: Dogs) {
        dogs.removeAll(where: { $0.uuid == dog.uuid }) // remove by checking the uuid field
        PersistenceController.shared.delete(dog)
        //self.readRecords() ... there's no need to re-read the entire array here
    }

without knowing what else you're doing in the app, this will probably work for you as long as all CRUD operations go through the view model.

however, if you ever use NSPersistentCloudKitContainer to share data across personal devices (which implies some cloud syncing happening in the background), or if your app has other views where you make changes to dogs in the database while the View associated with your view model is still alive in SwiftUI, this view model will not see external changes.

hope that helps,

DMG

1      

Thanks @delawaremathguy. Your answer helped a log.

   

Hacking with Swift is sponsored by Play

SPONSORED Play is the first native iOS design tool created for designers and engineers. You can install Play for iOS and iPad today and sign up to check out the Beta of our macOS app with SwiftUI code export. We're also hiring engineers!

Click to learn more about Play!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.