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 ?? "")
}
}
}
If you go to the Apple Docs and scroll down to the "Manage the User Session" header and look at the second paragraph:
The authentication request could be failing due to Apple rate-limiting you.