-1

I'm trying to implement authorization via Apple account, I've made several options from different sources, but none of the options worked. The problem is the following when I click on the authorization button I enter the password from the account and if the password is correct just nothing happens, control does not go to the method of processing the request didCompleteWithAuthorization, but if this window is closed, the method of request didCompleteWithError will work. Maybe someone has encountered this? I will be glad to be helped. Here is the code to familiarize you:

Button(action: {
                Task{
                    do{
                        try await viewModel.singInApple()
                        //UserDefaults.standard.set(true, forKey: "status")
                        //NotificationCenter.default.post(name: NSNotification.Name("statusChange"), object: nil)
                    } catch{
                        print(error)
                    }
                }
            }, label: {
                SignInWithAppleButtonViewRepresentable(type: .continue, style: .black).allowsHitTesting(false)
            })
            .frame(height: 45).onChange(of: viewModel.didSignInWithApple){
                newValue in
                if newValue{
                    UserDefaults.standard.set(true, forKey: "status")
                    NotificationCenter.default.post(name: NSNotification.Name("statusChange"), object: nil)
                }
            }

@available(iOS 13.0, *)
extension AuthenticationViewModel: ASAuthorizationControllerDelegate {
    
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    
      guard
        let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential,
        let appleIDToken = appleIDCredential.identityToken,
        let idTokenString = String(data: appleIDToken, encoding: .utf8),
        let nonce = currentNonce else{
          print("error")
          return
      }
      let tokens = SingInWithAppleResults(token: idTokenString, nonce: nonce,  appleIDCredential: appleIDCredential)
      
      Task{
          do{
              try await AuthenticationManager.shared.signInWithApple(tokens: tokens)
              didSignInWithApple = true
          } catch{
              
          }
      }
  }
    
  func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
    // Handle error.
    print("Sign in with Apple errored: \(error)")
  }
}

extension UIViewController: ASAuthorizationControllerPresentationContextProviding{
    public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return self.view.window!
    }
}

@MainActor
final class AuthenticationViewModel: NSObject ,ObservableObject{
    @Published var showAlert = false
    @Published var didSignInWithApple: Bool = false
    private var currentNonce: String?
 func singInApple() async throws{
        startSignInWithAppleFlow()
    }
    
    func startSignInWithAppleFlow() {
        guard let topVC = Utilities.shared.topViewController() else{
            return
        }
            
      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 = topVC
      authorizationController.performRequests()
    }
    
    private func randomNonceString(length: Int = 32) -> String {
      precondition(length > 0)
      var randomBytes = [UInt8](repeating: 0, count: length)
      let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes)
      if errorCode != errSecSuccess {
        fatalError(
          "Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)"
        )
      }

      let charset: [Character] =
        Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")

      let nonce = randomBytes.map { byte in
        // Pick a random character from the set, wrapping around if needed.
        charset[Int(byte) % charset.count]
      }

      return String(nonce)
    }
    
    @available(iOS 13, *)
    private func sha256(_ input: String) -> String {
      let inputData = Data(input.utf8)
      let hashedData = SHA256.hash(data: inputData)
      let hashString = hashedData.compactMap {
        String(format: "%02x", $0)
      }.joined()

      return hashString
    }
}

struct SingInWithAppleResults{
    let token: String
    let nonce: String
    let appleIDCredential: ASAuthorizationAppleIDCredential
}
}

struct SignInWithAppleButtonViewRepresentable: UIViewRepresentable{
    
    let type: ASAuthorizationAppleIDButton.ButtonType
    let style: ASAuthorizationAppleIDButton.Style
    
    func makeUIView(context: Context) -> ASAuthorizationAppleIDButton {
        ASAuthorizationAppleIDButton(authorizationButtonType: type, authorizationButtonStyle: style)
    }
    
    func updateUIView(_ uiView: ASAuthorizationAppleIDButton, context: Context) {
        
    }
}

final class Utilities{
    static let shared = Utilities()
    private init(){}
    
    @MainActor
    func topViewController(controller: UIViewController? = nil) -> UIViewController? {
        let controller = controller ?? UIApplication.shared.keyWindow?.rootViewController
        
        if let navigationController = controller as? UINavigationController{
            return topViewController(controller: navigationController.visibleViewController)
        }
        if let tabController = controller as? UITabBarController{
            if let selected = tabController.selectedViewController{
                return topViewController(controller: selected)
            }
        }
        if let presented = controller?.presentedViewController{
            return topViewController(controller: presented)
        }
        return controller
    }
}

    @discardableResult
    func signInWithApple(tokens: SingInWithAppleResults) async throws -> AuthDataResultModel?{
        let credential = OAuthProvider.appleCredential(withIDToken: tokens.token, rawNonce: tokens.nonce, fullName: tokens.appleIDCredential.fullName)
  return try await signIn(credential: credential)
    }

If I enter the wrong password, the password field is erased, if I enter the correct password nothing happens

Daxelarne
  • 172
  • 11
  • Since you are using SwiftUI you can get rid of most of your code if you use `SignInWithAppleButton` you'll just need the code that comes after you get the credential – lorem ipsum Aug 03 '23 at 16:01

0 Answers0