在 .sheet (SwiftUI) 上使用带有苹果的标志

时间:2020-12-20 15:48:22

标签: ios swiftui sign-in-with-apple

我所有的登录方法(如 fb 和 google 登录)都有协调器类并在 .sheet 上工作。 我试图从 this video 对这个视图做出同样的处理,但我被卡住了,根本找不到解决方案。我需要这个来从其他视图传递变量。

struct SignInWithAppleView: View {     
        @Environment (\.presentationMode) var presentationMode
        @State var coordinator: signInWithAppleCoordinator?
        var body: some View {
            VStack {
                SignInWithAppleButton()
                    .frame(height: 45)
                    .padding(.horizontal)
                    .onTapGesture {
                        self.coordinator = signInWithAppleCoordinator()
                        if let coordinator = self.coordinator {
                            coordinator.startSignInWithAppleFlow {
                                print("You successfuly signed in with apple")
                                self.presentationMode.wrappedValue.dismiss()
                            }
                        }
                    }
            }
        }
    }

我的谷歌登录代码示例如下

import SwiftUI
import GoogleSignIn
import Firebase

struct GoogleAuthView: UIViewRepresentable {
    
    @Binding var showAnimation: Bool
    @Binding var showSheet: Bool
    
    
    init(showAnimation: Binding<Bool>, showSheet: Binding<Bool>) {
        self._showAnimation = showAnimation
        self._showSheet = showSheet
    }
    
    func makeCoordinator() -> GoogleAuthView.Coordinator {
        return GoogleAuthView.Coordinator(showAnimation: self.$showAnimation, showSheet: self.$showSheet)
    }
    
    class Coordinator: NSObject, GIDSignInDelegate {
        
        @Binding var showAnimation: Bool
        @Binding var showSheet: Bool
        
        init(showAnimation: Binding<Bool>, showSheet: Binding<Bool>) {
            self._showAnimation = showAnimation
            self._showSheet = showSheet
        }
        
        
        
        func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error?) {
            if let error = error {
                print(error.localizedDescription)
                return
            }
            
            guard let authentication = user.authentication else {
                return
                
            }
            self.showAnimation = true
            self.showSheet = false
            let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
                                                           accessToken: authentication.accessToken)
            Auth.auth().signIn(with: credential) { (authResult, error) in
                if let error = error, (error as NSError).code == AuthErrorCode.credentialAlreadyInUse.rawValue {
                    Auth.auth().signIn(with: credential) { result, error in
                        // continue
                        print("signIn result: " + authResult!.user.email!)
                        if let token = firebaseRegistrationPushToken {
                            checkUserAuthSettings(pushToken: token)
                        }
                    }
                } else {
                    // continue
                    print("Facebook Sign In")
                    if let token = firebaseRegistrationPushToken {
                        checkUserAuthSettings(pushToken: token)
                    }
                }

            }
            
        }
    }
    
    func makeUIView(context: UIViewRepresentableContext<GoogleAuthView>) -> GIDSignInButton {
        let view = GIDSignInButton()
        GIDSignIn.sharedInstance().delegate = context.coordinator
        GIDSignIn.sharedInstance()?.presentingViewController = UIApplication.shared.windows.last?.rootViewController
        return view
    }
    
    func updateUIView(_ uiView: GIDSignInButton, context: UIViewRepresentableContext<GoogleAuthView>) { }
}

苹果登录视频的完整代码

import Foundation
import SwiftUI
import AuthenticationServices
import CryptoKit
import Firebase

struct SignInWithAppleButton: UIViewRepresentable {
    
    func makeUIView(context: Context) -> ASAuthorizationAppleIDButton {
        return ASAuthorizationAppleIDButton(type: .signIn, style: .black)
    }
    
    func updateUIView(_ uiView: ASAuthorizationAppleIDButton, context: Context) {
    }
}


class signInWithAppleCoordinator:NSObject, ASAuthorizationControllerPresentationContextProviding {
    
    private var onSignedIn: (() -> Void)?
    
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return UIApplication.shared.windows.first!
    }
    

    // Unhashed nonce.
    fileprivate var currentNonce: String?

    @available(iOS 13, *)
    func startSignInWithAppleFlow(onSignedIn:@escaping() ->Void ) {
        self.onSignedIn = onSignedIn
      let nonce = randomNonceString()
      currentNonce = nonce
      let appleIDProvider = ASAuthorizationAppleIDProvider()
      let request = appleIDProvider.createRequest()
      request.requestedScopes = [.fullName, .email]
      request.nonce = 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 signInWithAppleCoordinator: 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 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
      }
      // Initialize a Firebase credential.
        
      let credential = OAuthProvider.credential(withProviderID: "apple.com",
                                                idToken: idTokenString,
                                                rawNonce: nonce)
        Auth.auth().currentUser?.link(with: credential, completion: { (authresult, error) in
            //фиксируем ошибку при заходе с разных поставщиков на тот же ящик
            if let error = error, (error as NSError).code == AuthErrorCode.credentialAlreadyInUse.rawValue {
                print("The user you're trying to sign in with has already been linked")
                if let updatedCredential = (error as NSError).userInfo[AuthErrorUserInfoUpdatedCredentialKey] as? OAuthCredential {
                    print("Signing in with using updating credentials")
                    Auth.auth().signIn(with: updatedCredential) { (authResult, error) in
                        if let user = authResult?.user {
                            if let callback = self.onSignedIn {
                                callback()
                            }
                            
                        }
                    }
                }
            }
            else {
                if let callback = self.onSignedIn {
                    callback()
                }
            }
        })
    }
  }

  func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
    // Handle error.
    print("Sign in with Apple errored: \(error)")
  }

}

// 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
}

struct SignInWithAppleView: View {
    @EnvironmentObject var session: SessionStore
    
    @Environment (\.presentationMode) var presentationMode
    @State var coordinator: signInWithAppleCoordinator?
    var body: some View {
        VStack {
            SignInWithAppleButton()
                .frame(height: 45)
                .padding(.horizontal)
                .onTapGesture {
                    self.coordinator = signInWithAppleCoordinator()
                    if let coordinator = self.coordinator {
                        coordinator.startSignInWithAppleFlow {
                            print("You successfuly signed in with apple")
                            self.presentationMode.wrappedValue.dismiss()
                        }
                    }
                }
        }
    }
}

0 个答案:

没有答案