google sign in sign in flow canceled iOS - ios

I'm writing an iOS app that has to use google sign in. It compiles fine but when it runs and I try to sign in with a button, the sign in flow is interrupted:
This is the error: Optional("The operation couldn’t be completed. (com.google.GIDSignIn error -4.)")
error with signing in: Optional(Error Domain=com.google.GIDSignIn Code=-5 "The user canceled the sign-in flow." UserInfo={NSLocalizedDescription=The user canceled the sign-in flow.})
In the simulator, when I press the button, it will show the dialog box asking whether it is ok for the app to access google and then it will just disappear after a second or two.
Here is my ViewController:
//
// ViewController.swift
// frcscout
//
// Created by Elliot Scher on 12/20/22..
//
import UIKit
import GoogleSignIn
import GTMSessionFetcher
import GoogleAPIClientForREST
class ViewController: UIViewController {
#IBOutlet weak var signInButton: UIButton!
private let service = GTLRSheetsService()
override func viewDidLoad() {
super.viewDidLoad()
googleSignIn { signInStatus in
if signInStatus == true {
self.signInButton?.setTitle("Sign out", for: .normal)
print("Signed in!")
} else {
self.signInButton?.setTitle("Sign in", for: .normal)
print("Issues with signing in...")
}
}
}
#IBAction func signInPressed(_ sender: UIButton) {
print("Sign in pressed")
let user = GIDSignIn.sharedInstance.currentUser
if (user == nil) {
googleSignIn() { success in
if success == true {
sender.setTitle("Sign out", for: UIControl.State.normal)
} else {
return
}
}
} else {
GIDSignIn.sharedInstance.signOut()
self.service.authorizer = nil
sender.setTitle("Sign in", for: UIControl.State.normal)
}
}
}
extension ViewController {
func googleSignIn(completionHandler: #escaping (Bool) -> Void) {
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
if error == nil {
print("Managed to restore previous sign in!\nScopes: \(String(describing: user?.grantedScopes))")
self.requestScopes(googleUser: user!) { success in
if success == true {
completionHandler(true)
} else {
completionHandler(false)
}
}
} else {
print("No previous user!\nThis is the error: \(String(describing: error?.localizedDescription))")
let signInConfig = GIDConfiguration.init(clientID: K.clientID)
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { gUser, signInError in
if signInError == nil {
self.requestScopes(googleUser: gUser!) { signInSuccess in
if signInSuccess == true {
completionHandler(true)
} else {
completionHandler(false)
}
}
} else {
print("error with signing in: \(String(describing: signInError)) ")
self.service.authorizer = nil
completionHandler(false)
}
}
}
}
}
func requestScopes(googleUser: GIDGoogleUser, completionHandler: #escaping (Bool) -> Void) {
let grantedScopes = googleUser.grantedScopes
if grantedScopes == nil || !grantedScopes!.contains(K.grantedScopes) {
let additionalScopes = K.additionalScopes
GIDSignIn.sharedInstance.addScopes(additionalScopes, presenting: self) { user, scopeError in
if scopeError == nil {
user?.authentication.do { authentication, err in
if err == nil {
guard let authentication = authentication else { return }
// Get the access token to attach it to a REST or gRPC request.
// let accessToken = authentication.accessToken
let authorizer = authentication.fetcherAuthorizer()
self.service.authorizer = authorizer
completionHandler(true)
} else {
print("Error with auth: \(String(describing: err?.localizedDescription))")
completionHandler(false)
}
}
} else {
completionHandler(false)
print("Error with adding scopes: \(String(describing: scopeError?.localizedDescription))")
}
}
} else {
print("Already contains the scopes!")
completionHandler(true)
}
}
}
Can anyone help me fix this? Thanks!!

Related

Only instance methods can be declared #IBAction error?

I am facing this error on build for the function shown in the code
Only instance methods can be declared #IBAction
this error is coming up only after I introduced google sign in method for similar functionality , earlier it not an error
#IBAction func SignInButtonAction(_ sender: Any) {
guard let email = emailField.text else { return }
guard let pass = passwordField.text else { return }
Auth.auth().signIn(withEmail: email, password: pass) { user, error in
if error == nil && user != nil {
let setupcheckref = Firestore.firestore().collection("users").document(Auth.auth().currentUser!.uid)
setupcheckref.getDocument{(document, error) in
if let document = document, document.exists{
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
self.checksetup = document.get("setupComplete") as! Bool
if self.checksetup == true {
if Auth.auth().currentUser!.isEmailVerified {
self.performSegue(withIdentifier: "toLoginFeed", sender: self)
}
else{
print("please verify your email")
try! Auth.auth().signOut()
}
}
else{
self.view.makeToast("Please Setup Your Account!", duration: 2.5)
self.performSegue(withIdentifier: "fromlogintosetup", sender: self)
SVProgressHUD.dismiss()
} }
}
// self.dismiss(animated: false, completion: nil)
} else {
print("Error logging in: \(error!.localizedDescription)")
// self.resetForm()
// SVProgressHUD.dismiss()
}
}
}
That means you can create #IBActions only as instance methods of a class.
You might be creating it of a class.
class VC: UIViewController {
#IBAction func SignInButtonAction(_ sender: Any) {
//your code...
}
}

iOS Local Biometric authentication dialogue called again and again

Below is my code. When ever I run this code and validate the app using TouchID, TouchID Authentication dialog is dismissed and viewDidLoad() is called again which in turn shows the TouchID alert again. So I am not able to leave this page and stuck in a loop. Any help would be appreciated.
Note: Same code was working fine 2 days ago.
override func viewDidLoad() {
super.viewDidLoad()
initialSetup()
checkAuthenticationMethod()
}
private func checkAuthenticationMethod() {
let biometricsEnabled = UserDefaults.standard.bool(forKey: LocalDefaults.biometricsEnabled.rawValue)
if biometricsEnabled {
OperationQueue.main.addOperation {
self.setupLocalAuthentication()
}
}
}
private func setupLocalAuthentication() {
var error: NSError?
context.localizedCancelTitle = "Login using your PIN"
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
if let err = error {
self.showAlert(message: err.localizedDescription, withTitle: "Error", willViewPop: false)
}else {
self.localAuthenticationMessage.isHidden = false
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Unlock App") { (success, error) in
DispatchQueue.main.async { [weak self] in
self?.view.isUserInteractionEnabled = !success
if success {
self?.showHUDLoader(containerView: nil,message: "Logging in", enableInteraction: false)
self?.pinImage.image = UIImage(named: "PIN_4")
self?.updateUserLoginToken()
}else if let err = error {
self?.pinImage.image = UIImage(named: "PIN_0")
let errorCode = (err as NSError).code
if errorCode != -4 {
self?.localAuthenticationMessage.isHidden = true
}
}
}
}
}
}
}

Firebase email verification always return false user not verified. iOS

I am implementing firebase email verification, but when user received an email it's redirecting to app which is fine. When I am trying to sign in with verified email Auth.auth().currentUser?.isEmailVerified always return false. Below is my code:
class ViewController: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var signInBtn: UIButton!
var link: String!
override func viewDidLoad() {
super.viewDidLoad()
if let link = UserDefaults.standard.value(forKey: "Link") as? String{
self.link = link
signInBtn.isEnabled = true
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
#IBAction func didTapSignInWithEmailLink(_ sender: AnyObject) {
if let email = self.emailTextField.text {
Auth.auth().signIn(withEmail: email, link: self.link) { (user, error) in
if let error = error {
print("\(error.localizedDescription)")
return
}
}
} else {
print("Email can't be empty")
}
print(Auth.auth().currentUser?.email)
if Auth.auth().currentUser?.isEmailVerified == true {
print("User verified")
} else {
print("User not verified")
}
}
#IBAction func didTapSendSignInLink(_ sender: AnyObject) {
if let email = emailTextField.text {
let actionCodeSettings = ActionCodeSettings()
actionCodeSettings.url = URL(string: "https://example.firebaseapp.com")
// The sign-in operation has to always be completed in the app.
actionCodeSettings.handleCodeInApp = true
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
actionCodeSettings.setAndroidPackageName("com.example.android",
installIfNotAvailable: false, minimumVersion: "12")
Auth.auth().sendSignInLink(toEmail: email, actionCodeSettings: actionCodeSettings) { error in
if let error = error {
print("\(error.localizedDescription)")
return
}
print("Check your email for link")
}
} else {
print("Email cant be empty")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I've found the solution we need to reload the user, which will update the current user status. Call Auth.auth.currentUser.reload in didTapSignInWithEmailLink button func:
Auth.auth().currentUser?.reload(completion: { (error) in
if error == nil{
if let email = self.emailTextField.text {
Auth.auth().signIn(withEmail: email, link: self.link) { (user, error) in
if let error = error {
print("\(error.localizedDescription)")
return
}
}
} else {
print("Email can't be empty")
}
print(Auth.auth().currentUser?.email)
if Auth.auth().currentUser?.isEmailVerified == true {
print("User verified")
} else {
print("User not verified")
}
} else {
print(error?.localizedDescription)
}
})

Firebase Email Verification Swift

I tried to use the Firebase Authentication, but in the login lets me in even when I haven't used the email to confirm the verification.
I have two Viewcontroller , one for Login and the other for signUP.
I can log in and I get the email for verification, but I can also log in in without verification.
public func sendVerificationMail() {
if self.authUser != nil && !self.authUser!.isEmailVerified {
self.authUser!.sendEmailVerification(completion: { (error) in
// Notify the user that the mail has sent or couldn't because of an error.
})
} else {
// Either the user is not available, or the user is already verified.
}
}
#IBAction func signupButtonTapped(_ sender: Any) {
print("Sign up button tapped")
Auth.auth().createUser(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) { (user, error) in
if user != nil {
print("User has Signed Up")
self.sendVerificationMail()
}
if error != nil {
print("User cant Sign Up")
}
}
}
#IBAction func signinButtonTapped(_ sender: Any) {
Auth.auth().signIn(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) { (user, error) in
if user != nil {
print("User has Signed In")
}
if error != nil {
print("Cant Sign in user")
} else {
self.performSegue(withIdentifier: "toHome", sender: nil)
}
}
}
Firebase Auth doesn't prevent people from signing in when their email isn't verified. If you want to prevent users from advancing when they aren't verified, you will need to code this on the client using the isEmailVerified boolean.
Auth.auth().signIn(withEmail: self.userEmailTextField.text!, password: self.userPasswordTextField.text!) { (authResult, error) in
if let authResult = authResult {
let user = authResult.user
print("User has Signed In")
if user.isEmailVerified {
self.performSegue(withIdentifier: "toHome", sender: nil)
} else {
// do whatever you want to do when user isn't verified
}
}
if let error = error {
print("Cant Sign in user")
}
}
A more comprehensive solution than that provided by #jen-person (and based on together with another SO answer) is this:
final class MySignInView: UIView { // or possibly MySignInViewController: UIViewController
// ... your properties etc...
#IBAction func signInButtonPressed(sender _: AnyObject) {
trySigningIn()
}
}
// MARK: Private
private extension MySignInView {
func trySigningIn() {
guard
let email = userEmailTextField.text,
let password = userPasswordTextField.text
else {
print("Cannot sign in, email or password is 'nil'")
return
}
do {
try signIn(email: email, password: password) { [unowned self] authResult in
self.userDidSignIn(authResult.user)
}
} catch {
// Display error
}
}
func userDidSignIn(_ user: FIRUser) {
// Creds to Jen: https://stackoverflow.com/a/51389154/1311272
guard user.isEmailVerified else {
// TODO display message about non verified email user?
return
}
performSegue(withIdentifier: "toHome", sender: nil)
}
}
// MARK: Error
private extension MySignInView {
enum Error: Strng, Equatable {
case invalidEmail
case userDisabled
case wrongPassword
case userNotFound
case networkError
case unknownError
}
}
// MARK: Firebae specific
private extension MySignInView {
func signIn(
email: String,
password: String,
onSuccessful: (AuthDataResult) -> Void
) throws {
Auth.auth().signIn(
withEmail: email,
password: password
) { (authResult, anyError) in
if let anyError = anyError {
if let error = Error(anyError: anyError) {
throw error
} else {
fatalError("Unsupported error: \(anyError)")
}
}
onSuccessful(authResult)
}
}
}
private extension MySignInView.Error {
init?(anyError: Swift.Error) {
guard let authErrorCode = FIRAuthErrorCode(rawValue: anyError.code) else {
return nil
}
self.init(fireBaseAuthErrorCode: authErrorCode)
}
// Creds goes to to: https://stackoverflow.com/a/39936083/1311272
init(fireBaseAuthErrorCode: FIRAuthErrorCode) {
switch errCode {
case .ErrorCodeInvalidEmail:
self = .invalidEmail
case .ErrorCodeUserDisabled:
self = .userDisabled
case .ErrorCodeWrongPassword:
self = .wrongPassword
case .ErrorCodeUserNotFound:
self = .userNotFound
case .ErrorCodeNetworkError:
self = .networkError
default:
self = .unknownError
}
}
}
You need to inform the user appropriately about the different errors/incorrect states of course, not only print.
This code should probably me moved to a ViewModel, and if your are not using MVVM, I highly recommend it :).
if let email = emailTextfield.text, let password = passwordTextfield.text {
Auth.auth().signIn(withEmail: email, password: password) { authResult, error in
if let e = error{
print(e.localizedDescription)
}
else {
//Do whatever you want to do after successful login
}
}
}
You can try this also:
if( !Auth.auth().currentUser!.isEmailVerified) {
// do something
}

UISwitch().setOn does not work (seemly)

here is the uiswitch valueChange action
#IBAction func swichTouchIdState(sender: UISwitch) {
print("newState:\(sender.on)")
if sender.on == false {
AuthenticationSetter.touchIdAuthentication() {
isSuccess, error in
if isSuccess == true {
AuthenticationSetter.setAuthenticationSwitchUserDefault(false)
} else {
sender.setOn(true, animated: true)
}
if let laError = error as? LAError {
print("error happened in authenticating: \(laError)")
switch laError {
case .AppCancel:
print("App Cancel")
self.touchIdSwitch.setOn(true, animated: true)
break
case .SystemCancel:
print("System Cancel")
self.touchIdSwitch.setOn(true, animated: true)
break
case .UserCancel:
print("User Cancel")
dispatch_async(dispatch_get_main_queue()) {
self.touchIdSwitch.setOn(true, animated: false)
}
break
default:
break
}
} else {
print("no error")
}
}
} else {
AuthenticationSetter.setAuthenticationSwitchUserDefault(true)
}
}
and here is the AuthenticationSetter.touchIdAuthentication function
class func touchIdAuthentication(doAfterAuthentication: (Bool, NSError?) -> Void) {
let laContext = LAContext()
var error: NSError?
if laContext.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
laContext.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "TouchID Authentication", reply: doAfterAuthentication)
} else {
print("device don't support touchId")
}
}
and when I touch cancel at the touchid window, the "User Cancel" is printed but the switch doesn't become on.
I create a new demoViewController that has a UISwitch and a button
when the buttonpressed this function is called : demoSwitch.setOn(demoSwitch.on, true)
and It run just well!!!! can any ios master help me!

Resources