SwiftUI List not updating after Data change. (Needs switching the view)

Viewed 1038

I'm trying to change the SwiftUI list to update after tapping the checkbox button in my list. When I tap on a list row checkbox button, I call a function to set the immediate rows as checked which ID is less then the selected one. I could modify the ArrayList as selected = 0 to selected = 1. But as my list is Published var it should emit the change to my list view. but it doesn't.

Here's what I've done:

// ViewModel

import Foundation
import Combine

class BillingViewModel: ObservableObject {
    
    @Published var invoiceList = [InvoiceModel](){
        didSet {
            self.didChange.send(true)
        }
    }
    
    @Published var shouldShow = true
    
    let didChange = PassthroughSubject<Bool, Never>()
    
    init() {
        setValues()
    }
    
    func setValues() {
        for i in 0...10 {
            invoiceList.append(InvoiceModel(ispID: 100+i, selected: 0, invoiceNo: "Invoice No: \(100+i)"))
        }
    }
    
    func getCombinedBalance(ispID: Int) {
        DispatchQueue.main.async {
            
            if let row = self.invoiceList.firstIndex(where: {$0.ispID == ispID}) {
                self.changeSelection(row: row)
            }
            
        }
    }
    
    func changeSelection(row: Int) {
        if invoiceList[row].selected == 0 {
            
            let selectedRows = invoiceList.map({ $0.ispID ?? 0 <= invoiceList[row].ispID ?? 0 })
            
            print(selectedRows)
            
            for index in 0..<invoiceList.count {
                if selectedRows[index] {
                    invoiceList[index].selected = 1
                } else {
                    invoiceList[index].selected = 0
                }
            }
            
        } else {
            
            let selectedRows = invoiceList.map({ $0.ispID ?? 0 <= invoiceList[row].ispID ?? 0 })
            
            print(selectedRows)
            
            for index in 0..<invoiceList.count {
                if selectedRows[index] {
                    invoiceList[index].selected = 1

                } else {
                    invoiceList[index].selected = 0
                }
            }
        }
    }
    
}

// List View

import SwiftUI

struct ContentView: View {
    
    @StateObject var billingViewModel = BillingViewModel()
    @State var shouldShow = true
    
    var body: some View {
        NavigationView {
            ZStack {
                VStack {
                    List {
                        ForEach(billingViewModel.invoiceList) { invoice in
                            NavigationLink(
                                destination: InvoiceDetailsView(billingViewModel: billingViewModel)) {
                                invoiceRowView(billingViewModel: billingViewModel, invoice: invoice)
                            }
                        }
                        
                    }
                }
            }.navigationBarTitle(Text("Invoice List"))
        }
        
    }
}

// Invoice Row View

import SwiftUI

struct invoiceRowView: View {
    
    @StateObject var billingViewModel: BillingViewModel
    @State var invoice: InvoiceModel
    
    var body: some View {
        VStack(alignment: .leading) {
            HStack {
                Button(action: {
                    if invoice.selected == 0 {
                        print(invoice)
                        billingViewModel.getCombinedBalance(ispID: invoice.ispID ?? 0)
                    } else {
                        print(invoice)
                        billingViewModel.getCombinedBalance(ispID: invoice.ispID ?? 0)
                    }
                }, label: {
                    Image(systemName: invoice.selected == 0 ? "checkmark.circle" : "checkmark.circle.fill")
                        .resizable()
                        .frame(width: 32, height: 32, alignment: .center)
                    
                }).buttonStyle(PlainButtonStyle())
                
                Text(invoice.invoiceNo ?? "Hello, World!").padding()
                
                Spacer()
            }
        }
    }
}

// Data Model

import Foundation

struct InvoiceModel: Codable, Identifiable {
    var id = UUID()
    var ispID: Int?
    var selected: Int?
    var invoiceNo: String?
}
1 Answers

You need to use binding instead of externally injected state (which is set just to copy of value), i.e.

struct invoiceRowView: View {
    
    @StateObject var billingViewModel: BillingViewModel
    @Binding var invoice: InvoiceModel

    // .. other code

and thus inject binding in parent view

ForEach(billingViewModel.invoiceList.indices, id: \.self) { index in
    NavigationLink(
        destination: InvoiceDetailsView(billingViewModel: billingViewModel)) {
        invoiceRowView(billingViewModel: billingViewModel, invoice: $billingViewModel.invoiceList[index])
    }
}
Related