Sign-in with apple function not triggering

952 views Asked by At

I am trying to implement apple sign-in like the code below, i've already added capability in xcode and created certificate in apple developer page. I rewrite this code under a controller class and still nothing. It doesnt trigger any code block nor showing the button style in ui. What would be the problem ?

Helper class:

import Foundation
import AuthenticationServices
import CryptoKit
import Firebase

@available(iOS 13.0, *)
class AppleAuthHelper: NSObject {
    static let shared = AppleAuthHelper()
    private override init() {
        super.init()
    }
    private var currentNonce: String?
    private var signupController: UIViewController? {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        return storyboard.instantiateViewController(withIdentifier: K.VC.signUpVC) as? SignUpViewController
    }
    @available(iOS 13.0, *)
    typealias AppleSignInCompletion = ((ASAuthorizationAppleIDCredential?, Error?) -> Void)
    var appleSignInCompletion: AppleSignInCompletion?

    @available(iOS 13.0, *)
    func signInWithApple(onView: UIView, completion: @escaping AppleSignInCompletion) {
        appleSignInCompletion = completion
        let signInWithAppleButton = ASAuthorizationAppleIDButton(authorizationButtonType: .signIn, authorizationButtonStyle: .white)
        var buttonRect = onView.frame
        buttonRect.origin.x = 0
        buttonRect.origin.y = 0
        signInWithAppleButton.frame = buttonRect
        signInWithAppleButton.addTarget(self, action: #selector(signInWithApplePressed), for: .touchUpInside)
        onView.addSubview(signInWithAppleButton)
      }
    
    @objc func signInWithApplePressed() {
        let nonce = randomNonceString()
        currentNonce = nonce
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.fullName, .email]
        request.nonce = self.sha256(nonce)
        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
      }
    
      @available(iOS 13, *)
      private func sha256(_ input: String) -> String {
          let inputData = Data(input.utf8)
          let hashedData = SHA256.hash(data: inputData)
          let hashString = hashedData.compactMap {
              return String(format: "%02x", $0)
          }.joined()
          return hashString
      }
}
@available(iOS 13.0, *)
extension AppleAuthHelper: ASAuthorizationControllerDelegate {
    func authorizationController(
        controller: ASAuthorizationController,
        didCompleteWithAuthorization authorization: ASAuthorization) {
        if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
            guard let nonce = currentNonce else {
                self.appleSignInCompletion?(nil, NSError.init(domain: "apple", code: 1001, userInfo: nil))
                fatalError("Invalid state: A login callback was received, but no login request was sent.")
            }
            debugPrint("nonce is: \(nonce)")
            guard let appleIDToken = appleIDCredential.identityToken else {
                print("Unable to fetch identity token")
                self.appleSignInCompletion?(nil, NSError.init(domain: "apple", code: 1001, userInfo: nil))
                return
            }
            guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
                print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
                self.appleSignInCompletion?(nil, NSError.init(domain: "apple", code: 1001, userInfo: nil))
                return
            }
            debugPrint("idTokenString is: \(idTokenString)")
            //For Firebase use `credential`
            //Just Uncomment credential code
            // for firebase you have to install firebase pod & normal login you don't need to install pod just use socialId
            // socialId = appleIDCredential.user
            // Here Apple `appleIDCredential` (ASAuthorizationAppleIDCredential) will give you login user details
            // You can modify callback according to your requirment
            
             let credential = OAuthProvider.credential(
                 withProviderID: "apple.com",
                 idToken: idTokenString,
                 rawNonce: nonce)
            
             Auth.auth().signIn(with: credential) { (authDataResult, error) in
                if let user = authDataResult?.user {
                    print("You are signed-in with \(user.email ?? "unknown")")
                    guard let uid = Auth.auth().currentUser?.uid else {return}
                    
                    let user = User(email: user.email,
                                    userName: user.displayName,
                                    fullName: user.displayName)
                   
                    let db = Firestore.firestore()
                    do {
                        try db.collection("users").document(uid).setData(from: user, merge: true)
                    } catch {
                        print(error)
                    }
                }
                
                // Mak a request to set user's display name on Firebase
                 let changeRequest = authDataResult?.user.createProfileChangeRequest()
                 changeRequest?.displayName = appleIDCredential.fullName?.givenName
                 changeRequest?.commitChanges(completion: { (error) in
                    if let error = error {
                        print(error.localizedDescription)
                        
                    }else {
                        print("Updated display name: \(Auth.auth().currentUser!.displayName!)")
                    }
                 })
             }
            
            // Initialize a Firebase credential.
            self.appleSignInCompletion?(appleIDCredential, nil)
        }
    }
    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        // Handle error.
        self.appleSignInCompletion?(nil, error)
        print("Sign in with Apple errored: \(error)")
    }
}
@available(iOS 13.0, *)
extension AppleAuthHelper:
ASAuthorizationControllerPresentationContextProviding {
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        // Give your window object
        return signupController?.view.window ?? UIWindow()
    }
    private func randomNonceString(length: Int = 32) -> String {
        precondition(length > 0)
        let charset: Array<Character> =
            Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
        var result = ""
        var remainingLength = length
        while remainingLength > 0 {
            let randoms: [UInt8] = (0 ..< 16).map { _ in
                var random: UInt8 = 0
                let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
                if errorCode != errSecSuccess {
                    fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)")
                }
                return random
            }
            randoms.forEach { random in
                if remainingLength == 0 {
                    return
                }
                if random < charset.count {
                    result.append(charset[Int(random)])
                    remainingLength -= 1
                }
            }
        }
        return result
    }
}

Controller:

class SignUpViewController: UIViewController {
    
//    MARK: - Outlets
    @IBOutlet weak var navBar: UINavigationBar!
    @IBOutlet weak var signInWithApple: UIView!
    @IBOutlet weak var signInWithGoogleButton: UIButton!
    
//    MARK: - Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        navBar.topItem?.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(closeButtonTapped))
    }

//    MARK: - Buttons
    @objc func closeButtonTapped() {
        dismiss(animated: true, completion: nil)
    }
    
    @IBAction func googleSignInButtonTapped(_ sender: UIButton) {
        GoogleSignInManager.shared.googleSignIn(self)
    }
    
    private func signInWithAppleButtonTapped() {
        AppleAuthHelper.shared.signInWithApple(onView: self.signInWithApple) { (userCredential, error) in
            if let error = error {
                debugPrint(error)
                return
            }
            debugPrint(userCredential?.fullName ?? "")
            debugPrint(userCredential?.user ?? "")
            debugPrint(userCredential?.email ?? "")
        }
    }
}
2

There are 2 answers

0
Jacolack On

If you go to the Apple Docs and scroll down to the "Manage the User Session" header and look at the second paragraph:

calling for a new identity token on every launch, or more frequently than once a day, can result in your request failing due to throttling.

The authentication request could be failing due to Apple rate-limiting you.

0
Reece On

Are you running the simulator? This seems to be a known problem: https://developer.apple.com/forums/thread/651533?page=13 to the extent where apple is rejecting apps for not properly implementing Sign In With apple; devs are sending them this link ^^ then finally publishing the app.