Check if Email from Apple Sign in is registered in Firebase

Viewed 2070

Inside my app the user can Sign in with Apple. Creating the Account works fine (using Firebase) . However before the user actually signs in I would like to check wether or not the user is already registered or not. For that I need the email. The problem is that I can not access the email properly as I am getting a found nil- error.

This is my function that should handle the check/signIn:

// delegate functions
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
  if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
    guard let nonce = currentNonce else {
      fatalError("Invalid state: A login callback was received, but no login request was sent.")
    }
    guard let appleIDToken = appleIDCredential.identityToken else {
      print("Unable to fetch identity token")
      return
    }
    guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
      print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
      return
    }

    let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)
    print("email: " + appleIDCredential.email!)
    let email = appleIDCredential.email

    Auth.auth().fetchSignInMethods(forEmail: email!) { (methods, error) in

         if error != nil {
            // show error popUp
             Utilities.showErrorPopUp(labelContent: "Fehler", description: error!.localizedDescription)
         } else {
             // no error -> check email adress

             // Email is not registered -> sign up
             if methods == nil {

                 print("signed in")
                 let usernameVC = self.storyboard?.instantiateViewController(withIdentifier: "UsernameVC") as! UserNameVC
                 usernameVC.credential = credential
                 usernameVC.signInOption = "apple"
                 self.navigationController?.pushViewController(usernameVC, animated: true)

             }
             // Email is registered -> login
             else {

                Auth.auth().signIn(with: credential, completion: { (user, error) in
                    if error != nil {
                        Utilities.showErrorPopUp(labelContent: "Fehler beim Login", description: error!.localizedDescription)
                    } else {

                        // set user status to logged-in
                        UserDefaults.standard.setIsLoggedIn(value: true)
                        UserDefaults.standard.synchronize()

                        // stop animation
                        self.logoAnimation.stop()

                        //transition to home
                        self.transitionToHome()
                    }
                })
             }
         }
    }
  }
}

Like I said this not working when the user is already signed in, it only works if the user is signing up the very first time, otherwise there seems to be a problem with getting the Email.

Does anyone know a workaround or anything on how to fix this issue?

3 Answers

If you can not access the email property in credential, means that user already signed in with his appleId in the application once before, so you can make decision if that user registered in this app already or not:

// .....

guard let email = appleIDCredential.email else {
    // User already signed in with this appleId once
    Auth.auth().signIn(with: credential, completion: { (user, error) in
        // .......
    })
    return
}

Auth.auth().fetchSignInMethods(forEmail: email) { (methods, error) in
    // .......
})

With @Nikita's help I finally got it working. As suggests I first check wether or not I can access the email. My final function looks like this:

let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)

    guard appleIDCredential.email != nil else {
        // User already signed in with this appleId once
        Auth.auth().signIn(with: credential, completion: { (user, error) in

            DataHandler.checkIfAppleUserExists(uid: (user?.user.uid)!) { (exists) in
                if exists { // user has firebase-profile -> login
                    if error != nil {
                        Utilities.showErrorPopUp(labelContent: "Fehler beim Login", description: error!.localizedDescription)
                    } else {

                        UserDefaults.standard.setIsLoggedIn(value: true)
                        UserDefaults.standard.synchronize()

                        self.logoAnimation.stop()

                        self.transitionToHome()
                    }
                } else { // user doesn't have firebase-profile -> create username
                    let usernameVC = self.storyboard?.instantiateViewController(withIdentifier: "UsernameVC") as! UserNameVC
                    usernameVC.credential = credential
                    usernameVC.signInOption = "appleExists"
                    self.navigationController?.pushViewController(usernameVC, animated: true)
                }
            }

        })
        return
    }

    // first time apple sign in
    let usernameVC = self.storyboard?.instantiateViewController(withIdentifier: "UsernameVC") as! UserNameVC
    usernameVC.credential = credential
    usernameVC.signInOption = "apple"
    self.navigationController?.pushViewController(usernameVC, animated: true)
  }

I also implemented the helper function checkIfAppleUserExists which checks if the user was actually created inside Firebase. If so, the user logs in, otherwise he gets to the UsernameVC in which the Firebase-Profile gets created.

If you want to check if the user is already signed in You can check it like this:

import FirebaseAuth

if Auth.auth().currentUser != nil {
// the user is signed in
} else {
// the user in not signed in
}
Related