Resend Code using Firebase Authentication in Swift - ios

As there is no direct method to resend code once the code has already been sent , I am trying to resend it once again with following method :
func sendOTPGetVerfID(phoneNum: String, completion: #escaping((_ verificationID: String?, _ error: Error?) -> Void)) {
PhoneAuthProvider.provider().verifyPhoneNumber( phoneNum , uiDelegate: nil) { (verificationID, error) in
if let error = error {
QL1(error.localizedDescription)
completion(nil, error)
return
}
// Sign in using the verificationID and the code sent to the user
// ...
QL1("VerificationID : \(verificationID ?? "")")
completion(verificationID,nil)
}
}
Issue here is, I am not able to receive the verificationID second time, calling the above method
Any help appreciated

Call requestOtp() method on resend OTP button click.
import FirebaseAuth
func requestOtp() {
let phNo = "Your phone number"
PhoneAuthProvider.provider().verifyPhoneNumber(phNo, uiDelegate: nil) { (verificationID, error) in
if let error = error {
print(error.localizedDescription)
return
}
// Sign in using the verificationid and the code sent to the user
// ...
UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
}
}
Verify Code with Auth:
func verifyNumberWith(verificationCode: String) {
let verificationID=UserDefaults.standard.string(forKey:"authVerificationID")
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationID ?? "",
verificationCode: verificationCode)
Auth.auth().signIn(with: credential) { (authResult, error) in
//Do your actions.
}
}

Related

Firebase Mobile not logging in after authentication

How to prevent the account from automatically logging in after firebase SMS authentication and registration, I tried to delete ->Auth.auth().signIn(with: credential) { _, error in completion(error) } , but the firebase account registration was unsuccessful
#IBAction func startVerification() {
let configuration = Configuration(requestCode: { phone, completion in
PhoneAuthProvider.provider().verifyPhoneNumber(phone, uiDelegate: nil, completion: completion)
}, signIn: { verificationID, verificationCode, completion in
let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationID, verificationCode: verificationCode)
Auth.auth().signIn(with: credential) { _, error in completion(error) }
})
let vc = PhoneVerificationController(configuration: configuration)
vc.delegate = self
present(vc, animated: true)
}
}

Firebase google signin authentication AppDelegate- Use of unresolved identifier 'isMFAEnabled'

I'm new to iOS development. I am trying to add google sign in to my app but i am facing an some problems.Code shows some "Use of unresolved identifier 'isMFAEnabled"and "Value of type 'AppDelegate' has no member 'showTextInputPrompt'".Please help me.I'm following this doc- https://firebase.google.com/docs/auth/ios/google-signin#swift_9 enter image description here
import UIKit
import Firebase
import GoogleSignIn
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,GIDSignInDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return GIDSignIn.sharedInstance().handle(url)
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let error = error {
print(error.localizedDescription)
return
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
let authError = error as NSError
if (isMFAEnabled && authError.code == AuthErrorCode.secondFactorRequired.rawValue) {
// The user is a multi-factor user. Second factor challenge is required.
let resolver = authError.userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
var displayNameString = ""
for tmpFactorInfo in (resolver.hints) {
displayNameString += tmpFactorInfo.displayName ?? ""
displayNameString += " "
}
self.showTextInputPrompt(withMessage: "Select factor to sign in\n\(displayNameString)", completionBlock: { userPressedOK, displayName in
var selectedHint: PhoneMultiFactorInfo?
for tmpFactorInfo in resolver.hints {
if (displayName == tmpFactorInfo.displayName) {
selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo
}
}
PhoneAuthProvider.provider().verifyPhoneNumber(with: selectedHint!, uiDelegate: nil, multiFactorSession: resolver.session) { verificationID, error in
if error != nil {
print("Multi factor start sign in failed. Error: \(error.debugDescription)")
} else {
self.showTextInputPrompt(withMessage: "Verification code for \(selectedHint?.displayName ?? "")", completionBlock: { userPressedOK, verificationCode in
let credential: PhoneAuthCredential? = PhoneAuthProvider.provider().credential(withVerificationID: verificationID!, verificationCode: verificationCode!)
let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator.assertion(with: credential!)
resolver.resolveSignIn(with: assertion!) { authResult, error in
if error != nil {
print("Multi factor finanlize sign in failed. Error: \(error.debugDescription)")
} else {
self.navigationController?.popViewController(animated: true)
}
}
})
}
}
})
} else {
print(error.localizedDescription)
return
}
// ...
return
}
// User is signed in
// ...
}
}
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
So the boilerplate is wayyyy more code than you need. This is my review of your AppDelegate.
didFinishLaunchingWithOptions looks good as-is.
Pro tip: add this line for staying logged in GIDSignIn.sharedInstance()?.restorePreviousSignIn()
open,options looks good as-is.
didSignInFor user is where it gets tangled. Delete the entire function and replace it with the following extension (outside the class' brackets):
(also delete GIDSignInDelegate in the class protocols)
extension AppDelegate: GIDSignInDelegate {
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
//handle sign-in errors
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("The user has not signed in before or they have since signed out.")
} else {
print("error signing into Google \(error.localizedDescription)")
}
return
}
// Get credential object using Google ID token and Google access token
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
// Authenticate with Firebase using the credential object
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
print("authentication error \(error.localizedDescription)")
}
}
}
}
I have tested this in today and it's currently working (Swift 5/ios 13.6).
TL;DR; you can just delete that variable if you don't want to enable multi-factor authentication.
The Firebase documentation provides the variable as a way for you to enable/disable multi-factor (MF) authentication (i.e. when facebook sends you a text message for you to verify). It's more like they are giving you an incomplete template that doesn't compile unless you declare and set this variable (among other things like implementing showTextInputPrompt).
The code provided in firebase documentation is an example, so don't expect it to work out of the box.

How can I call for a segue in the AppDelegate?

The segue needs to take place only after the user has been authenticated with their Google account. This authentication process takes place in the AppDelegate. I have a segue between two view controllers rather than a button so when the button is tapped, the segue will only be called for once.
The following code takes place within the sign function in the AppDelegate for anyone who is familiar with Firebase:
Auth.auth().signIn(with: credential) { (authResult, error) in
if let error = error {
print("Firebase sign In error")
print(error)
return
} else {
let db = Firestore.firestore()
db.collection("users").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
print("Accessing email")
for document in querySnapshot!.documents {
if document.get("uid") as? String != Auth.auth().currentUser?.uid {
db.collection("users").addDocument(data: ["firstName": firstName!, "lastName": lastName!, "email": email!, "uid": authResult!.user.uid]) { (error) in
if error != nil {
print("Error: User data not saved")
return
}
}
}
}
}
}
self.window?.rootViewController!.performSegue(withIdentifier: "googleSegue", sender: nil)
print("User is signed in with Firebase")
}
}
}
However the self.window?.rootViewController!.performSegue(withIdentifier: "googleSegue", sender: nil) does nothing when it should be occurring and taking the user to the connected view controller after they sign in. The print statement does occur so there is nothing wrong in that regard.
For reference, this is what happens in the viewController file:
#IBAction func googleSignInTapped(_ sender: Any) {
print("User has chosen to sign with Google.")
GIDSignIn.sharedInstance().signIn()
}

Firebase Login URL Fetch Config Error

I am trying to have my login function for firebase run after updating it, but the function is nil and I am getting the following messages:
Cannot create a URL to fetch a configuration with empty app instance ID or empty platform (app instance, platform): (nil), ios
[Firebase/Analytics][I-ACS023125] Unable to get configuration. Invalid server URL.
My code is as follows:
#IBAction func Login(sender: AnyObject) {
let email = self._Email.text!
let password = self._Password.text!
print("below is the email")
print(email)
print("below is the password")
print(password)
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
if error == nil {
print("Successful login")
if user!.isEmailVerified{
let vc = self.storyboard!.instantiateViewController(withIdentifier: "ProfileView") as! ProfileView
self.present(vc, animated: true, completion: nil)
} else {
print("nil is hitting")
}
} else {
print("Some nil error")
}
}
}

Terminating app due to uncaught exception Swift

I currently have login authentication working, as well as registration with firebase. I now wanted to keep my user logged in, which is working. The problem I am facing is when I log out, I try to log back in and I receive this error:
// Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver () has no segue with identifier 'chatRoom''\
Which does not make sense, because when I first log in, it brings me to the ChatRoom Viewcontroller using the chatRoom segue.
This is my code below:
func handleLogin() {
guard let email = emailTextField.text, let password = passwordTextField.text
else {
print("Form is not valid")
return
}
Auth.auth().signIn(withEmail: email, password: password, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
self.loginErrorAlert("Error!", message: "Username or password incorrect, please try again.")
return
}
// successfully logged in our user and keep user logged in until they logout
if Auth.auth().currentUser != nil {
UserDefaults.standard.set(Auth.auth().currentUser!.uid, forKey: "loggedIn")
UserDefaults.standard.synchronize()
self.performSegue(withIdentifier: "chatRoom", sender: self)// this is the error I am facing
}
})
}
func handleRegister() {
guard let email = emailTextField.text, let password = passwordTextField.text, let name = nameTextField.text else {
print("Form is not valid")
signupErrorAlert("Error!", message: "Could not be Registered at this time, please try again.")
return
}
Auth.auth().createUser(withEmail: email, password: password, completion: { (user: User?, error) in
if error != nil {
print(error!.localizedDescription)
self.loginErrorAlert("Error!", message: "Could not be Registered at this time, please try again later.")
return
}
guard let uid = user?.uid else {
return
}
// successfully authenticated user and keep logged in until they logout
let ref = Database.database().reference(fromURL: "https://boccighub.firebaseio.com/")
let usersReference = ref.child("users").child(uid)
let values = ["name": name, "email": email]
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
if err != nil {
print(err!.localizedDescription)
return
}
if Auth.auth().currentUser != nil {
UserDefaults.standard.set(Auth.auth().currentUser!.uid, forKey: "loggedIn")
UserDefaults.standard.synchronize()
self.performSegue(withIdentifier: "chatRoom", sender: self)
}
})
})
}
// User logged out
#IBAction func handleLogout(_ sender: Any) {
do {
try Auth.auth().signOut()
print("user signedout")
if Auth.auth().currentUser == nil {
print("No user, key removed")
UserDefaults.standard.removeObject(forKey: "loggedIn")
UserDefaults.standard.synchronize()
print("User logged out")
let loginController = LoginViewController()
present(loginController, animated: true, completion: nil)
}
} catch let logoutError {
print(logoutError)
}
}
Fixed. I Removed the let loginController = LoginViewController(),
I created a new segue and now do self.performSegue(withIdentifier: "logOut", sender: self), works perfectly

Resources