0

I am trying to get the collection from firebase firestore. Below is my ViewModel I am unable to store the data in the @Published var categories object. I am unable to find the exact issue, I am a bit new to swift but the exact same thing was working a while back is there something I am doing wrong?

FormViewModel.swift

import Firebase
import FirebaseFirestore

class FormViewModel: ObservableObject {
    @Published var categories = [Category]()
    private var db = Firestore.firestore()
    
    func fetchCategories() {
        db.collection("companies_by_category").addSnapshotListener { (QuerySnapshot, error) in
            guard let documents = QuerySnapshot?.documents else {
                print("No documents")
                return
            }

            self.categories = documents.compactMap { (QueryDocumentSnapshot) -> Category? in
                return try? QueryDocumentSnapshot.data(as: Category.self)
            }
            print(self.categories) // -> Able to get data
        }
    }
}

My CategoriesView

struct CategoriesView: View {
    
    @ObservedObject var viewmodel = FormViewModel()
    
    var body: some View {
        ScrollView {
            List(self.viewmodel.categories) { category in
                ZStack {
                    RoundedRectangle(cornerRadius: 15, style: .continuous)
                        //.foregroundColor(category.selected == true ? Color.yellow.opacity(0.5) : Color.white)
                        .foregroundColor(.white)
                        .shadow(color: Color.black.opacity(0.1), radius: 10, x: 0, y: 0)
                    
                    VStack(spacing: 20) {
                        HStack {
                            Image("\(category.category)")
                                .foregroundColor(Color.gray)
                                .font(.system(size: 20.0))
                            Text(category.category)
                                .font(.system(size: 20.0, weight: .medium))
                                .foregroundColor(Color.black)
                            Spacer()
                        }
                    }
                .padding()
                }
            }
        }
        .onAppear() {
            self.viewmodel.fetchCategories()
        }
    }
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • 1
    This is because you are doing an asynchronous call to your db so the print is executed before the data is returned. You should not be concerned with this since `categories` is published so any observer will be notified when the array gets values. – Joakim Danielson Sep 14 '20 at 13:59
  • http://www.programmingios.net/what-asynchronous-means/ – matt Sep 14 '20 at 14:09
  • Thanks I missed the fact that it was asynchronous, but as David explained in the answer the issue is not with the @ Published, but with the @ observed object as I am creating it in view itself. – Rahul Bajaj Sep 14 '20 at 14:18

1 Answers1

0

You are missing the fact that addSnapshotListener is an async function. Your second print will return no data, because it gets executed synchronously before the async network request of addSnapshotListener could return any data. You can only access the async data of addSnapshotListener inside the closure of the function.

However, the reason for your actual view not getting updated is different from the above. You are correctly updating the categories array from inside the async closure, so whenever the async request finishes, your categories will contain the correct data.

The UI issue is coming from the fact that you create your @ ObservedObject property inside your view. This is completely incorrect, since whenever an @ObservedObject property of a View updates, the view will be redrawn, which will result in its properties also being recalculated. You can fix this by injecting your viewmodel in the View's init. Check this answer for a more detailed explanation of how you should inject @ObservedObjects into Views.

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • Hey thanks for the answer, My view is still not showing even after initializing it in the main view as - ` MainView - @State var formViewModel = FormViewModel() CategoriesView(viewModel: self.formViewModel) ` CategoriesView - ` @ObserverdObject var viewmodel: FormViewModel ` – Rahul Bajaj Sep 15 '20 at 03:58
  • @RahulBajaj did you try debugging your code? Is `categories` actually updated and containing elements? If you put a breakpoint in the `body` of `CategoriesView`, does it get hit after `categories` was updated? – Dávid Pásztor Sep 15 '20 at 08:33