skip to Main Content

I have a tax Create/Edit View, and List View,View Model Class as Observable Object.

I update values to core data in viewModel class from an edit view, that updated values are not reflecting in list view which uses the same view Model class.
The list view only updated when I go back to other screen and come to this list screen screen or if restart the app

Please kindly suggest

List View Code

struct TaxListView: View {
    
    @State var searchTerm:String = ""
    @State var isTaxSelected = false
    @State var seletedTax:TaxCodesList = TaxCodesList(businessId: "", taxID: "", taxName: "", taxRate: 0.0, taxtype: "", taxCodes: [TaxCodeList]())

    @ObservedObject var taxViewModel = TaxViewModel()
    @Environment(.presentationMode) var mode
    
var body : some View {
    
    VStack{
        List{
            ForEach(taxViewModel.arrTaxCodes, id: .taxID){ tax in
                TaxlistRow(tax: tax, selectedTax: $seletedTax, taxSelected: $isTaxSelected)
            }
        }
    }
    
}

View Model Class

class TaxViewModel:ObservableObject {
    
    @Published var arrTaxCodes:[TaxCodesList] = []
    @Published var arrTempTaxCodes:[TaxCodesList] = []
    
    let context = PersistenceController.shared.container.viewContext
    
    func getTaxCodes(){
        let fetchRequest : NSFetchRequest<TaxCodes> = TaxCodes.fetchRequest()
            fetchRequest.predicate = NSPredicate(format: "businessId = %@", Constants.businessID ?? "")
        
        do {
            let result = try context.fetch(fetchRequest)
            arrTaxCodes.removeAll()
            for data in result {
                arrTaxCodes.append(TaxCodesList(responseObj: data))
            }
            arrTempTaxCodes = arrTaxCodes
            
        } catch  {
            print(error.localizedDescription)
        }
    }
    
    //MARK: Update Tax
    func updateTax(tax:TaxCodesList, completion:@escaping (Bool,String) -> Void) {
        
        let fetchRequest : NSFetchRequest<TaxCodes> = TaxCodes.fetchRequest()
        fetchRequest.predicate = NSPredicate(format: "taxID = %@", tax.taxID)
        
        do {
            let result = try context.fetch(fetchRequest)
            
            guard let objectToUpdate = result.first else {
                return
            }
            
            var taxCodesObj = [[String:Any]()]
            ///// assigning values to taxCodesObj 
 
            objectToUpdate.setValue(tax.taxName ,forKey: "taxName")
            objectToUpdate.setValue(tax.taxRate, forKey: "taxRate")
            objectToUpdate.setValue(tax.taxType, forKey: "taxType")
            objectToUpdate.setValue(["taxCodes":taxCodesObj], forKey: "taxCodes")
            
            do {
                try context.save()
                //Context.refreshAllObjects()
                completion(true,"Tax Updated Successfully")
            } catch {
                completion(true,error.localizedDescription)
                print(error.localizedDescription)
            }

        } catch  {
            completion(true,"TaxId not Found")
            print(error.localizedDescription)
        } 
    }   
}

Edit View

struct CreateTaxView: View {

    ///// Some Variables
    
    @ObservedObject var taxViewModel:TaxViewModel
    
    @Environment(.presentationMode) var mode
    
    func saveTax(){
        
        //// Codes to create tax Object
            taxViewModel.updateTax(tax: tax) { flag, msg in
                print(msg)
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
                    self.mode.wrappedValue.dismiss()
                })
            }   
        } 
    
    var body: some View {
        Form {
            Section {
                ///// Some Views
            }
            .onAppear{
                taxViewModel.getTaxCodes(Context: viewContext)
                if isFromUpdate && isInitialLoad {
                 /////Assign values to variables
                }
            }
        }
        .navigationBarTitle(Text(navTitle))
        .navigationBarItems(trailing: Button{
            saveTax()
        } label:{
            Text("Save")
        } )
            
    }
    
    func delete(at offsets:IndexSet){
        taxCodes.remove(atOffsets: offsets)
    }

TaxCodesList Struct

struct TaxCodesList{
    
    var businessId:String
    var taxID:String
    var taxName:String
    var taxRate: Double
    var taxType:String
    var taxCodes:[TaxCodeList]
    
    ////Init code
    

    //Initialize from core data object
    init(responseObj:TaxCodes){
        self.businessId = responseObj.businessId ?? ""
        self.taxID = responseObj.taxID ?? ""
        self.taxName = responseObj.taxName ?? ""
        self.taxRate = responseObj.taxRate
        self.taxType = responseObj.taxType ?? ""
        
        let payload = responseObj.taxCodes?["taxCodes"] as? [[String:Any]] ?? [[String:Any]()]
        var tempArr = [TaxCodeList]()
        tempArr.removeAll()
        for data in payload {
            if !(data["taxId"] as? String ?? "").isEmpty {
                tempArr.append(TaxCodeList(responseObj: data))
            }
        }
        self.taxCodes = tempArr
    }
    
}

2

Answers


  1. I missed the code also for TaxCodeList (without the "s"), but I think I found the problem: in your view model you are reading from CoreData and passing the values of your TaxCodes entity to a struct called TaxCodesList (with the "s"). Then, your view model is publishing the changes from an array of structs.

    Your views are reading data that comes from the array of the TaxCodesList struct, not from the Core Data object. Structs cannot publish changes, so your view cannot update when you change Core Data – the changes are not reaching the view.

    Your code is a bit long for me to give you the solution, but the first step is to have your views reading form an ObservableObject, which can be either a class or the Core Data object directly. Depending on what you want to achieve, you could turn your TaxCodesList into a class, or – my personal preference – turn TaxCodesList into a Core Data entity and create a relationship between this new entity and TaxCodes.

    Login or Signup to reply.
  2. Call

    getTaxCodes()
    

    When you want to see an update.

    No part of your code is observing the persistent store. Your list doesn’t know when to get the new values

    You completely disconnect from CoreData with the View Model and struct. If you are doing this for abstraction and not unintentionally it’s fine but you have to somehow listen to the store so you can recreate everything.

    Using something in the View like onChange may or may not provide updates when needed. The store can change from many different directions that your abstraction layer will be unaware of.

    This may offer you an alternative for listening.

    If this is an unintentional pattern use the CoreData object directly by wrapping it with an

    @ObservedObject
    

    That way you can make changes and see them right away.

    The link above also has a more direct setup for CoreData and has a simple

    CoreDataPersistence
    

    Object that does all the heavy lifting for any object.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search