I am having trouble retrieving Firestore getdocument data before the view loads. I know that it returning values from multiple checks and there is probably a problem with how I handle the asynchronous functions.
My three variables that I am trying to set.
@Published var numParticipants = 0
@Published var totalAnswered = 0
@Published var showResults = false
This is the first function that gets and sets the number of participants variable.
func getRoom(roomId: String, onSuccess: @escaping(_ room: Room) -> Void) {
DB.collection("Rooms").document(roomId).addSnapshotListener { document, error in
DispatchQueue.main.async {
if let dict = document?.data() {
guard let decodeRoom = try? Room.init(fromDictionary: dict) else { return }
onSuccess(decodeRoom)
}
}
}
}
This the second function that gets and sets the total answered variable
func getNumParticipants(roomId: String, onSuccess: @escaping(_ numParticipants: Int) -> Void) {
DB.collection("RoomsParticipants").document(roomId).collection("Participants").getDocuments { snapshot, error in
DispatchQueue.main.async {
if let error = error {
print(error.localizedDescription)
return
} else {
onSuccess(snapshot!.count)
}
}
}
}
And then I use this last function to compare the two variables and load the view if they are equal, otherwise just wait until they are equal.
func checkShowResults(roomId: String) {
isLoading = true
self.getNumParticipants(roomId: roomId) { numParticipants in
print("Number of docs: \(numParticipants)")
DispatchQueue.main.async {
self.numParticipants = numParticipants
}
}
self.getRoom(roomId: roomId) { room in
print("Total answered: \(room.totalAnswered)")
DispatchQueue.main.async {
self.totalAnswered = room.totalAnswered
if self.totalAnswered == self.numParticipants {
self.showResults = true
}
}
}
isLoading = false
}
Here is the Results View that I am trying to display based off the fetched data.
struct ResultsView: View {
@StateObject var resultsViewModel = ResultsViewModel()
var roomId: String
var body: some View {
VStack {
if !resultsViewModel.showResults {
VStack {
ProgressView()
Text("Waiting for all participants \nto finish answering...")
}
} else {
ShowResultsView()
}
}
}
}.navigationBarHidden(true)
.onAppear {
resultsViewModel.checkShowResults(roomId: roomId)
}
}
Even if the totalAnswered and numParticipants are equal when the view initially displayed, showResults is always set to false. But when the data changes it eventually gets set to true if they become equal again. I think this is because the API call to firebase/firestore is taking time and the variables aren't set before the view loads. I don't really want to use async/await.
CodePudding user response:
Currently your code execute self.getNumParticipants(..)
independently of self.getRoom(roomId: roomId)
.
However in checkShowResults
, self.getRoom(roomId: roomId)
depends
on self.numParticipants
that you get from self.getNumParticipants(..)
.
So you could try nesting your functions calls. Something like the following code:
func checkShowResults(roomId: String) {
self.isLoading = true
self.getNumParticipants(roomId: roomId) { numParticipants in
print("Number of docs: \(numParticipants)")
DispatchQueue.main.async {
self.numParticipants = numParticipants
// get the room after the numParticipants has been set
self.getRoom(roomId: roomId) { room in
print("Total answered: \(room.totalAnswered)")
DispatchQueue.main.async {
self.totalAnswered = room.totalAnswered
if self.totalAnswered == self.numParticipants {
self.showResults = true
self.isLoading = false
}
}
}
}
}