Swift中Firebase单函数更新邮箱用户名:回调未执行求助
Ah, I spot the immediate issue here—your updateDetails function never invokes the updateComplete closure you pass into it! That’s exactly why the callback code in saveChangesButtonPressed never runs, even though the auth and database updates work as expected.
Let’s break this down and fix it properly:
1. Core Problem: Missing Closure Invocation
Looking through your updateDetails implementation, there’s nowhere you call updateComplete(error:)—not in the email update success/failure blocks, nor in the display name update blocks. The closure is passed into the function, but never triggered, so the code waiting for it in saveChangesButtonPressed has no way to execute.
2. Bonus Issue: Uncoordinated Async Operations
On top of that, you’re running two independent async operations (updateEmail and commitChanges) in parallel. Right now, if one fails and the other succeeds, your user gets mixed error alerts, and you can’t properly report the overall result to the caller. We should coordinate these tasks so we only notify the caller once both operations are done.
Fixed Code Implementation
Here’s how to adjust updateDetails to fix both issues, using a DispatchGroup to wait for both async tasks to complete:
func updateDetails(withEmail email: String, andDisplayName displayName: String, updateComplete: @escaping(_ error: Error?) -> ()) { guard let currentUser = Auth.auth().currentUser, let userId = user?.uid else { updateComplete(NSError(domain: "AuthError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Unable to access current user"])) return } let group = DispatchGroup() var combinedError: Error? // Update email group.enter() currentUser.updateEmail(to: email) { error in defer { group.leave() } if let error = error { combinedError = error } else { let ref = Database.database().reference().child("Users").child(userId).child("email") ref.setValue(currentUser.email) } } // Update display name group.enter() let changeRequest = currentUser.createProfileChangeRequest() changeRequest.displayName = displayName changeRequest.commitChanges { error in defer { group.leave() } if let error = error { combinedError = error } else { let ref = Database.database().reference().child("Users").child(userId).child("name") ref.setValue(currentUser.displayName) DispatchQueue.main.async { self.welcomeLabel.text = "Hi, \(displayName)" } } } // Wait for both tasks to finish, then call the completion closure group.notify(queue: .main) { updateComplete(combinedError) } }
You can keep your saveChangesButtonPressed mostly the same—now the callback will actually fire as expected:
@IBAction func saveChangesButtonPressed(_ sender: Any) { guard let email = emailTextField.text, !email.isEmpty, let displayName = currentUserNameTextField.text, !displayName.isEmpty else { createError(title: "Error", message: "Email and name fields must not be blank") return } updateDetails(withEmail: email, andDisplayName: displayName) { error in if let error = error { self.createError(title: "Update Failed", message: error.localizedDescription) } else { self.createError(title: "Success", message: "Your details have been updated!") } } }
Key Changes Explained
- Added closure invocation: We now call
updateCompleteinsidegroup.notifyonce both async tasks are done, passing along any error that occurred. - Coordinated async tasks:
DispatchGroupensures we wait for both the email and display name updates to finish before notifying the caller. - Moved error handling to caller: Instead of showing error alerts inside
updateDetails, we letsaveChangesButtonPressedhandle feedback—this keeps your code more modular and reusable. - Safe optional unwrapping: Added guard clauses to handle cases where
currentUseroruser?.uidmight be nil, which prevents crashes and properly reports errors.
内容的提问来源于stack exchange,提问作者Rob

