问题描述
当我将 Sign In with Apple 连接到 Firebase 时,它出现错误消息“无法将类型为“LoginPopupViewController”的值分配给类型“ASAuthorizationControllerPresentationContextProviding?”它不会显示任何用户已登录 Firebase 控制台。
我遵循了本教程的第二部分:https://www.youtube.com/watch?v=BxQsdhglZtE
import Foundation
import UIKit
import AuthenticationServices
import FirebaseAuth
import Firebase
import FirebaseFirestore
import CryptoKit
class LoginPopupViewController: UIViewController,ASAuthorizationControllerDelegate {
@IBAction func doneBtn(_ sender: Any) {
dismiss(animated: true,completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
setupSignInButton()
}
func setupSignInButton() {
let button = ASAuthorizationAppleIDButton(type: .signIn,style: .white)
button.addTarget(self,action: #selector(handleSignInWithAppleTapped),for: .touchUpInside)
button.frame.size = CGSize(width: 300.0,height: 40.0)
button.center = view.center
view.addSubview(button)
}
@objc func handleSignInWithAppleTapped() {
performSignIn()
}
func performSignIn() {
let request = createAppleIDRequest()
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self
authorizationController.presentationContextProvider = self
authorizationController.performRequests()
}
func createAppleIDRequest() -> ASAuthorizationopenIDRequest {
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName,.email]
let nonce = randomNonceString()
request.nonce = sha256(nonce)
currentNonce = nonce
return request
}
}
extension ViewController: ASAuthorizationControllerDelegate {
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 recieved,but no login request was sent")
}
guard let appleIDToken = appleIDCredential.identityToken else {
print("Unable to fetch identify 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)
Auth.auth().signIn(with: credential) { (authDataResult,error) in
if let user = authDataResult?.user {
print("Nice! You're Now signed in as \(user.uid),email: \(user.email ?? "unkNown")")
}
}
}
}
}
extension ViewController: ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return self.view.window!
}
}
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
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
}
// Unhashed nonce.
fileprivate var currentNonce: String?
@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
}
解决方法
您需要 LoginPopupViewController
以符合 ASAuthorizationControllerPresentationContextProviding
extension LoginPopupViewController: ASAuthorizationControllerPresentationContextProviding {
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return self.view.window!
}
}
,
使用 FirebaseUI 更容易实现登录 Apple,我让 Auth 工作并绕过了预先构建的欢迎屏幕:
import Foundation
import UIKit
import AuthenticationServices
import FirebaseAuth
import Firebase
import FirebaseFirestore
import CryptoKit
import FirebaseUI
class LoginPopupViewController: UIViewController,ASAuthorizationControllerDelegate,FUIAuthDelegate {
@IBAction func doneBtn(_ sender: Any) {
dismiss(animated: true,completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
setupSignInButton()
}
func setupSignInButton() {
let button = ASAuthorizationAppleIDButton(type: .signIn,style: .white)
button.addTarget(self,action: #selector(handleSignInWithAppleTapped),for: .touchUpInside)
button.frame.size = CGSize(width: 300.0,height: 40.0)
button.center = view.center
view.addSubview(button)
}
@objc func handleSignInWithAppleTapped() {
if let authUI = FUIAuth.defaultAuthUI() {
authUI.providers = [FUIOAuth.appleAuthProvider()]
authUI.delegate = self
authUI.signIn(withProviderUI: FUIOAuth.appleAuthProvider(),presenting: self,defaultValue: nil)
// let authViewController = authUI.authViewController()
// self.present(authViewController,animated: true)
}
}
@objc(authUI:didSignInWithAuthDataResult:error:) func authUI(_ authUI: FUIAuth,didSignInWith authDataResult: AuthDataResult?,error: Error?) {
dismiss(animated: true,completion: nil)
if let user = authDataResult?.user {
print("Nice! You've signed in as \(user.uid). Your email is: \(user.email ?? "") ")
}
}
}