Home > Software design >  Completion Handler for Firebase/Firestore not working
Completion Handler for Firebase/Firestore not working

Time:12-19

I am using Firestore to retrieve some data about a user and just update a label to be the user's first name on viewDidLoad of the Home screen. However, I learned the label was getting loaded/updated before even receiving the data.

I knew I had to wait until we fully received the data from Firestore. I feel like completion handlers were my best bet and I feel like I have followed Swift completion handler documentation to a T, but I can't seem to get it to work! If someone could please tell me what I am doing wrong here. One important thing to keep in mind here is before I make HomeViewController the rootview controller and present it to the user after logging in, I am retrieving the Google firestore DocumentReference and setting userProfileDoc to this reference to be used by the HomeViewController class in a different method.

class HomeViewController: UIViewController {
    var userProfileDoc: DocumentReference?
    private var currentUser: UserProfile?

    @IBOutlet weak var userFirstName: UILabel!
    @IBOutlet weak var logoutButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        let myCompletionHandler: (UserProfile) -> Void = { theUser in
            self.userFirstName.text = "Welcome, "   theUser.userFirstName!
            self.userFirstName.sizeToFit()
        }

        initializeUserProfile(using: myCompletionHandler)
    }

    func initializeUserProfile(using completionHandler: (UserProfile) -> Void) {
        self.currentUser = UserProfile(userProfileDoc!)

        completionHandler(self.currentUser!)
    }
}

`

Below is the constructor of UserProfile `

class UserProfile {
    var userFirstName: String?
    var userLastName: String?
    var profilePicURL: String?
    var UID: String?

    init(_ documentRef: DocumentReference) {
        documentRef.getDocument { [self] document, error in
            if let error = error as NSError? {
                print("Error getting document: \(error.localizedDescription)")
            }
            else {
                if let document = document {
                    let data = document.data()
                    userFirstName = data?["firstName"] as? String ?? ""
                    userLastName = data?["lastName"] as? String ?? ""
                    profilePicURL = data?["profilePicURL"] as? String ?? ""
                    UID = data?["uid"] as? String ?? ""
                }
            }
        }
    }
}

CodePudding user response:

Don't make the asynchronous call in the init but move it to initializeUserProfile instead and call the completion handler from inside the closure of the asynchronous call like below.

func initializeUserProfile(_ documentRef: DocumentReference, using completionHandler: (UserProfile) -> Void) {
    documentRef.getDocument { [self] document, error in
        if let error = error as NSError? {
            print("Error getting document: \(error.localizedDescription)")
        } else {
            if let document = document {
                let data = document.data()
                let userFirstName = data?["firstName"] as? String ?? ""
                let userLastName = data?["lastName"] as? String ?? ""
                let profilePicURL = data?["profilePicURL"] as? String ?? ""
                let userID = data?["uid"] as? String ?? ""
                completionHandler(UserProfile(userFirstName: userFirstName,
                                              userLastName: userLastName,
                                              profilePicURL: profilePicURL,
                                              UID: userID))
            }
        }
    }
}

and call it from viewDidLoad

initializeUserProfile(userProfileDoc, using: myCompletionHandler)

Note that I don't use Firebase myself so I haven't been able to compile the code given here but it should be enough to explain how I think this should be solved and this also mean I don't know if there are other improvements that should be done.

For completeness, this is how I changed the UserProfile class

class UserProfile {
    var userFirstName: String?
    var userLastName: String?
    var profilePicURL: String?
    var userID: String?

    init(userFirstName: String?, userLastName: String?, profilePicURL: String?, userID: String?) {
        self.userFirstName = userFirstName
        self.userLastName = userLastName
        self.profilePicURL = profilePicURL
        self.userID = userID
    }
}
  • Related