如何实现仅邮箱验证通过后才创建用户并禁止未验证用户登录
Got it, let's tackle this problem head-on. The main issue right now is that Firebase Auth creates the user account immediately, and your app lets un-verified users access the main screen on restart—even though you haven't added them to your database yet. Here's how to fix this so users only get added to the database after verifying their email, and can't access the app until they do:
Step 1: Adjust the Registration Flow
We'll still create the Firebase Auth user and send the verification email, but we'll hold off on writing user data to your database until the email is verified. We'll also pass user details (name, country) through the timer's userInfo so we can use them later.
var verificationTimer = Timer() @objc func signupButtonPressed() { // Guard against empty fields with cleaner syntax guard let name = name.text, !name.isEmpty, let email = email.text, !email.isEmpty, let password = password.text, !password.isEmpty, let country = countryTextField.text, !country.isEmpty else { let emptyFieldAlert = UIAlertController(title: "Error", message: "Please fill out all the fields.", preferredStyle: .alert) emptyFieldAlert.addAction(UIAlertAction(title: "Ok", style: .cancel)) present(emptyFieldAlert, animated: true) return } // Create Auth user first, but don't write to database yet Auth.auth().createUser(withEmail: email, password: password) { [weak self] result, error in guard let self = self else { return } if let error = error { print("Signup failed: ", error.localizedDescription) let errorAlert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert) errorAlert.addAction(UIAlertAction(title: "Ok", style: .cancel)) self.present(errorAlert, animated: true) return } // Send verification email Auth.auth().currentUser?.sendEmailVerification(completion: { emailError in if let emailError = emailError { print("Failed to send verification email: ", emailError.localizedDescription) } }) // Start timer to check verification status, pass user details via userInfo self.verificationTimer = Timer.scheduledTimer( timeInterval: 1, target: self, selector: #selector(self.checkIfEmailIsVerified), userInfo: ["name": name, "country": country], repeats: true ) } }
Step 2: Update Verification Check to Write to Database
Once the user verifies their email, we'll stop the timer, write their details to your database, then navigate to the main screen.
@objc func checkIfEmailIsVerified() { guard let currentUser = Auth.auth().currentUser else { verificationTimer.invalidate() return } currentUser.reload { [weak self] error in guard let self = self else { return } if let error = error { print("Failed to reload user data: ", error.localizedDescription) return } if currentUser.isEmailVerified { self.verificationTimer.invalidate() // Extract user details from timer's userInfo let userName = self.verificationTimer.userInfo?["name"] as? String ?? "" let userCountry = self.verificationTimer.userInfo?["country"] as? String ?? "" // Prepare data for your database (adjust for Firestore, Realtime DB, etc.) let userData: [String: Any] = [ "uid": currentUser.uid, "name": userName, "email": currentUser.email ?? "", "country": userCountry, "createdAt": Date() ] // Write to database (example uses Firestore) let db = Firestore.firestore() db.collection("users").document(currentUser.uid).setData(userData) { dbError in if let dbError = dbError { print("Failed to save user to database: ", dbError.localizedDescription) } else { // Navigate to main screen only after successful database write let mainPage = MainScreen() self.present(mainPage, animated: true) } } } } }
Step 3: Block Un-Verified Users on App Restart
Update your authentication check to not only verify if a user exists, but also if their email is verified. If not, prompt them to verify or log out.
func authenticateUserAndConfigure() { guard let currentUser = Auth.auth().currentUser else { DispatchQueue.main.async { self.present(LoginScreen(), animated: false) } return } // Reload user to get the latest verification status (local cache might be outdated) currentUser.reload { [weak self] error in guard let self = self else { return } if let error = error { print("Failed to reload user: ", error.localizedDescription) DispatchQueue.main.async { self.present(LoginScreen(), animated: false) } return } if !currentUser.isEmailVerified { // Show prompt for un-verified users let verificationAlert = UIAlertController( title: "Email Not Verified", message: "Please verify your email to access the app. We've sent a verification link to your inbox.", preferredStyle: .alert ) verificationAlert.addAction(UIAlertAction(title: "Resend Verification", style: .default, handler: { _ in currentUser.sendEmailVerification(completion: { resendError in if let resendError = resendError { print("Failed to resend verification: ", resendError.localizedDescription) } else { let successAlert = UIAlertController(title: "Email Sent", message: "Check your inbox for the verification link.", preferredStyle: .alert) successAlert.addAction(UIAlertAction(title: "Ok", style: .cancel)) self.present(successAlert, animated: true) } }) })) verificationAlert.addAction(UIAlertAction(title: "Logout", style: .destructive, handler: { _ in do { try Auth.auth().signOut() DispatchQueue.main.async { self.present(LoginScreen(), animated: false) } } catch { print("Logout failed: ", error.localizedDescription) } })) DispatchQueue.main.async { self.present(verificationAlert, animated: true) } } else { // Optional: Double-check if user exists in your database before proceeding let db = Firestore.firestore() db.collection("users").document(currentUser.uid).getDocument { snapshot, dbError in if dbError != nil || snapshot?.exists == false { print("User not found in database") // Handle edge case (e.g., sign out user) } else { // User is verified and exists in database: proceed to main app DispatchQueue.main.async { // Your main screen setup logic here } } } } } }
Key Notes
- We use
[weak self]in closures to avoid memory leaks. - The timer's
userInfosafely passes user details from the registration flow to the verification check. - Reloading the user on app start ensures we get the latest verification status (Firebase caches this locally, so it might be stale).
内容的提问来源于stack exchange,提问作者Abdullah Ajmal

