0

I'm learning SwiftUI and working on a iOS app project with SwiftUI where I have used Core Data for persistent data storage. I've created a global view model and injected it into the environment from top so that I can access it in within my app from anywhere.

App.swift

@main
struct AppName: App {
    @StateObject var viewModel = AppViewModel()

    var body: some Scene {
        WindowGroup {
            HomeScreenView()
                .environmentObject(viewModel)
        }
    }

I've created @Published arrays which holds the existing objects of core data entities. I use them to display values inside my view.

ViewModel.swift

class AppViewModel: ObservableObject {
    @Published var entityAs: [EntityA] = []
    @Published var entityBs: [EntityB] = []
    @Published var entityCs: [EntityC] = []
    ...
    
    init() {
        //load data in variables
        loadEntityAs()
        loadEntityBs()
        loadEntityCs()
        ...
    }

    func loadEntityAs() {
        let request = NSFetchRequest<EntityA>(entityName: EntityNames.EntityA.rawValue)
        do {
            self.entityAs = try CoreDataManager.shared.context.fetch(request)
        } catch let error {
            print("Unable to fetch entity data: \(error.localizedDescription)")
        }
    }
    ...

I've used singleton for persistent data and call it when I've to save data. Persistence.swift

class CoreDataManager {
    static let shared = CoreDataManager() //Singleton
    let context: NSManagedObjectContext
    let container: NSPersistentContainer
    
    private init() {
        container = NSPersistentContainer(name: "App_Name")
        container.loadPersistentStores { description, error in
            if let error = error {
                print("Error loading core data: \(error)")
            }
        }
        self.context = container.viewContext
    }
    
    func save() {
        do {
            if context.hasChanges {
                try context.save()
            }
        } catch let error {
            print("Error saving Core Data: \(error.localizedDescription)")
        }
    }
    
    func delete(_ entity: NSManagedObject) {
        context.delete(entity)
        save()
    }
}

I'm now creating views where user can modify the existing values of entities, for e.g. changing name. I can think of 2 options. What's the best practice here?

  1. Creating a function called fetchEntityA(name: String) to fetch the required entity using its name. Then modify its value and then calling CoreDataManager.shared.save()

  2. Looping over @Published var entityAs and getting the required entity followed by changing its value and then saving it.

I'm not sure if option 2 will work or not. I've used option 1 in my last project and it works fine. But I'm not sure if I'm overdoing stuff here.

Also, is this method of using @Published var entityAs: [EntityA] good over using @FetchedResults inside SwiftUI Views? I've not used @FetchedResults before and I'm not sure how I should compare these 2 approaches.

Current Setup that I'm thinking of (option 1): EntityDetailsView.swift

struct EntityDetails: View {
    @EnvironmentObject var vm: AppViewModel
    let entity: EntityA
    @State var name = ""
    @State var type = ""

    var body: some View {
     VStack {
        Text("Entity Details")
        TextField(name, text: $name)
        TextField(type, text: $type)
        Button("Save") {
            vm.updateEntityA(entity, newName: name, newType: type)
        }
      }
       .onAppear {
           self.name = entity.name ?? ""
           self.type = entity.type ?? ""
       }
    }
}

I've previously used the first option, i.e. creating a function which will fetch required entity, modify its value and saving it. It works fine. But I want to explore other ideas, since writing these functions of fetching every entity I've in my app and writing update functions for each entity is a bit annoying. There must be a more optimal way of doing this stuff.

DevSec
  • 1
  • 2
  • If you use a view model then option 2 is what you should use, no point in fetching objects you already have access to. I am not sure though that a global view model is such a good idea and I would advise you to look into using FetchRequest in the view. If you have some advanced business logic you might need a view model or other kind of service class for a view but not one that only holds a published property with all the objects – Joakim Danielson Mar 02 '23 at 07:17

0 Answers0