Reputation: 373
I'm fetching some data from the Firebase database and would like to display them in a View. The fetching part works so far, but not displaying the data. Since I'm new to the MVVM pattern I'm a little bit confused where my mistake is. I think I'm missing something about the async calls, but I'm not sure.
For fetching the data I created a FirestoreManager:
class FirebaseManager {
private let db = Firestore.firestore()
func getUploadedDecks() -> [FlashcardDeck]{
var uploadedDecks = [FlashcardDeck]()
db.collection(K.FSCollectionName).getDocuments { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
fatalError("error downloading documents")
}
for document in documents{
if let deck = try? document.data(as: FlashcardDeck.self) {
uploadedDecks.append(deck)
print(deck.title) // <----- prints the ID correctly
}
}
}
return uploadedDecks
}
ViewModel:
class DownloadViewModel: ObservableObject {
@Published var decks = [FlashcardDeck]()
@Published var show = false
func fetchDecks(){
self.decks = FirebaseManager().getUploadedDecks()
}
}
View: Here appears only the "loading..." Textfield.
struct DownloadDecksView: View {
@ObservedObject var viewModel: DownloadViewModel
var body: some View {
VStack{
if viewModel.decks == [] {
Text("loading...")
} else {
ForEach(viewModel.decks) { elem in
Flashcard(elem)
}
}
.onAppear {
viewModel.fetchDecks()
}
}
MainView:
struct HomeView: View {
@StateObject var downloadViewModel = DownloadViewModel()
...
var body: some View {
DownloadDecksView(viewModel: downloadViewModel)
...
}
}
Upvotes: 2
Views: 801
Reputation: 257573
This is all about asynchronous retrieving, so you need to return results in callback, like
class FirebaseManager {
private let db = Firestore.firestore()
func getUploadedDecks(completion: @escaping ([FlashcardDeck]) -> ()) {
db.collection(K.FSCollectionName).getDocuments { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
fatalError("error downloading documents")
}
var uploadedDecks = [FlashcardDeck]()
for document in documents{
if let deck = try? document.data(as: FlashcardDeck.self) {
uploadedDecks.append(deck)
}
}
completion(uploadedDecks) // << here !!
}
// no return !!
}
and use it as
class DownloadViewModel: ObservableObject {
@Published var decks = [FlashcardDeck]()
@Published var show = false
func fetchDecks(){
FirebaseManager().getUploadedDecks() { [weak self] decks in
DispatchQueue.main.async {
// always update UI-linked properties on main queue
self?.decks = decks
}
}
}
}
Upvotes: 2