I am using Google Firestore to store data for my App's users. When the app loads, if a user is logged in, I want to get their data. I need to access three different documents, so I have created a Loading viewController that calls three functions like this:
override func viewDidLoad() {
super.viewDidLoad()
loadingFunction1()
loadingFucntion2()
loadingFunction3()
...
self.performSegue(withIdentifier: "goToNextScreen", sender: nil)
}
Each function looks a little like this:
func loadingFunction1() {
self.database.collection("doc").getDocuments() { (querySnapshot, error) in
...get document from Firestore and store it locally
}
}
I need to load all of the data before the segue takes the app to the next screen.
I have tried:
- completion handlers for each function - they each execute in the correct order, but the segue fires before they are all done.
- nesting completion handlers - putting each function in the completion handler for the prior function, still the segue fires before they are done.
- putting all three function calls in a DispatchGroup, still fires before they are done.
- instead of doing the DispatchGroup/Queue thing in the ViewController, I have done it inside each function in the class that holds the three functions. Still fires the segue before they are done.
I have followed Ray Wenderlich's tutorial on DispatchGroups (https://www.raywenderlich.com/148515/grand-central-dispatch-tutorial-swift-3-part-2)
I have tried this Stack Overflow question (iOS - swift 3 - DispatchGroup)
I have read DispatchGroup not working in Swift 3 and How do I use DispatchGroup / GCD to execute functions sequentially in swift? and how to use a completion handler to await the completion of a firestore request and I am still stumped.
How do I make my app execute each of the three functions completely before moving on to the next action. I don't even care what order the three functions are carried out, just so long as they are completely done before moving on.
BTW, my ViewController has a very nice animated Activity Indicator View to entertain the user while this is all happening.
UPDATE with Solution:
I adopted the array of Bools suggestion, with the didSet idea from the comments:
var completedRequests: [Bool] = [false, false, false] {
didSet {
segueWhenAllRequestsAreComplete()
}
}
However, that wasn't enough. I had to add both an escaping completion handler and a dispatchGroup to each function, like this:
func loadingFunction1(completion: @escaping (Bool) -> ()) {
DispatchQueue.global(qos: .userInteractive).async {
let downloadGroup = DispatchGroup()
var success:Bool = false
downloadGroup.enter()
self.database.collection("doc").getDocuments() { (querySnapshot, error) in
if error == nil {
...get document from Firestore and store it locally
success = true
}
downloadGroup.leave()
}
downloadGroup.wait()
DispatchQueue.main.async {
completion(success)
}
}
}
and then call the functions like this:
DataManager.shared.loadData { success in
self.completedRequests[0] = success
}
So now, at long last, the segue does not fire until all three functions are finished. Seems a little round about, but it works.