Home > Mobile >  How do I update a view after retrieving data from Firestore?
How do I update a view after retrieving data from Firestore?

Time:01-02

I have an iOS widget in which I would like to update the Widget View after I retrieve data from Firestore. I have looked all over the internet, including at this question. I am unable to update the view after the data is retrieved. Please look at the following code for the widget's view. The data is retrieved, but I am unable to update the UI element after I retrieve the data as I would like.

struct Runner_WidgetEntryView: View {
    @State var text = ""
    @State var textLoaded = false
    var entry: Provider.Entry
    
    var body: some View {
        ZStack {
            Image("back").resizable().scaledToFit()
            Rectangle().foregroundColor(.black).cornerRadius(10).padding([.leading, .trailing], 10).padding([.top, .bottom], 15).overlay(Text(textLoaded ? text : "Loading..."))
        }.onAppear {
            let dispatchGroup = DispatchGroup()
            FirebaseApp.configure()
            let db = Firestore.firestore()
            dispatchGroup.enter()
            db.collection("collection").getDocuments { (snapshot, error) in
                if error != nil {
                    text = "There was an error retrieving the text."
                    dispatchGroup.leave()
                }
                if let snapshot = snapshot {
                    for document in snapshot.documents {
                        if document.documentID == "Document ID" {
                            text = document.data()["qoute_name"] as! String
                            dispatchGroup.leave()
                        }
                    }
                }
                else {
                    text = "There was an error retrieving the text."
                    dispatchGroup.leave()
                }
            }
            dispatchGroup.notify(queue: .main) {
                textLoaded = true
            }
        }
    }
}

How do I update the TextView after I retrieve the data? Thanks in advance.

CodePudding user response:

SwiftUI needs to have its variables updated on the main thread. So if you are not seeing the update, it could be due to the different threads involved. Try this to update text on the main thread.

EDIT-1:

.onAppear {
    var fireTxt = ""  // <-- here
    let dispatchGroup = DispatchGroup()
    FirebaseApp.configure()
    let db = Firestore.firestore()
    dispatchGroup.enter()
    
    db.collection("collection").getDocuments { (snapshot, error) in
            if error != nil {
                fireTxt = "There was an error retrieving the text."  // <-- here
            }
            if let snapshot = snapshot {
                fireTxt = "No document found"  // <-- here default text
                for document in snapshot.documents {
                    if document.documentID == "Document ID" {
                        fireTxt = document.data()["qoute_name"] as! String   // <-- here
                    }
                }
            }
            else {
                fireTxt = "There was an error retrieving the text."  // <-- here
            }
            dispatchGroup.leave()
        }
    
    dispatchGroup.notify(queue: .main) {
        textLoaded = true
        text = fireTxt    // <-- here on the main thread
    }
}

Note, it is not a good idea to do all this db fetching in the .onAppear. You should use a view model for that.

CodePudding user response:

After building off of @workingdog's answer and a lot of testing, I have come up with this solution:

The SwiftUI state cannot be updated from a closure, so it cannot be updated from within dispatchGroup.notify or any other closure.

The workaround was to retrieve the data from Firebase when creating a timeline, and then saving the retrieved data to UserDefaults. From there, the data can be read from UserDefaults in the onAppear method in the SwiftUI view.

  • Related