Swiftui - Wait Until Firestore Getdocuments() Is Finished Before Moving On

How to properly set State with Firestore getDocuments

The only thing that's wrong is your expectations about how getDocuments works.

getDocuments is asynchronous, and the callback (completion) you pass to it is executed after it returns. Print something in the callback and it'll be more clear what's actually going on.

Given that, checkDoc cannot possibly return the correct value synchronously unless you do something to make it block (which you should not do). You will need to handle all query results asynchronously.

Slight delay when retrieving from Firestore

The only I would change is to update published properties on main queue, like

DispatchQueue.main.async {
self.userModel = .init(data: data)
}

// ...

DispatchQueue.main.async {
self.privateUserModel = .init(data:userEmail )
}

everything else I assume is just network delays.

addSnapShotListener triggers all the function dependent to it?

When the data changed (and at the initial load) only the code inside your listener (the ... in your question) is called. There is no effect on functions A or B. So any code that needs the data from the database, has to be inside the snapshot callback, be called from there, or be otherwise synchronized with that code.

If this is surprising to you, you may be new to dealing with asynchronous callbacks. If that's the case, I recommend checking out:

  • How do I save data from cloud firestore to a variable in swift?
  • Storing asynchronous Cloud Firestore query results in Swift
  • SwiftUI - Wait until Firestore getDocuments() is finished before moving on
  • How can I change the order of functions triggered?

SwiftUI & Firestore

There are a number of ways to do this but the two most common ways are to (1) use a launch screen to indicate a loading state that disappears to a view identical to the launch screen (to continue the appearance of loading) that is only removed when the database returns (i.e. Twitter); (2) load the user right into the app and allow them to move freely while either indicating that data is loading or displaying cached data.

Remember that Firestore maintains a local cache on the device which means that data will be available immediately when the app launches. This data may be out of sync with the server but it will update as soon as the app establishes a connection with Firestore, which is usually instant. What I would recommend is launching the user right into the app without the use of a loading screen and relying on the cached data to get the UI up as fast as possible.

And if we're only talking about user-specific data (data that is specific to the user that the user has full control over) then that data will only change when the user changes it, which would have been the last time they used the app, which means that the locally-cached data on their device (assuming they use only one device) will always reflect the state of the server (in theory, anyway). And if it doesn't then it doesn't; the fresh data will update instantly anyway.

You may then wonder what happens if the user launches the app without connection. In that case, the cached data is displayed and the user is almost none the wiser. And because Firestore is offline capable, the user can freely edit their data and it will write to the server when connection eventually establishes.

SwiftUI x Firebase - Trouble Waiting for Firebase Results before Executing Code

The code is the question is pretty close and you're kinda dealing with the asynchronous nature of Firebase correctly except one thing: code following a (Firebase) closure will execute before the code within the closure.

It takes time for Firebase to return results and code is faster than the internet. So you need to deal with Firebase data within it's closure (or within the completion handler in this case)

The fix is to handle data when it's available like this

findUser(searchKey: "email", searchValue: search) { (result) in
if result != nil {
print("user was found")
//go do something with the user
} else {
print("no user was found!")
}
}
//any code here will execute before the code in the above closure


Related Topics



Leave a reply



Submit