firebaseswiftuiasync-awaitsign-in-with-apple

SwiftUI return Bool from authorizationController async


How could I return a Bool from this sign in with apple function that uses the authorizationController? I have been looking at withCheckedThrowingContinuation but with no luck. I have also seen some examples of using completion handlers but I would like to use async.

func signInWithApple(withState state: SignInState) async -> Bool {
    let request = signInWithAppleRequest(withState: state)
    let authorizationController = ASAuthorizationController(authorizationRequests: [request])
    authorizationController.delegate = self
    authorizationController.performRequests()
}

func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
    
    //...
        
        let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)
        
        switch state {
        case .signIn:
            Task {
                authenticationState = .authenticating
                do {
                    let result = try await Auth.auth().signIn(with: credential)
                    if result.additionalUserInfo?.isNewUser == true {
                        self.isNewUser = true
                    }
                    return true
                } catch {
                    self.authenticationState = .unauthenticated
                    print(error.localizedDescription)
                    return false
                }
            }
            
        case .reauth:
            Task {
                if let user = Auth.auth().currentUser {
                    do {
                        try await user.reauthenticate(with: credential)
                        return true
                    } catch {
                        print(error.localizedDescription)
                        return false
                    }
                }
            }
        }
    }
}

Solution

  • I don't have all your types, so here is a simple example of a continuation which at least compiles.

    The class must inherit from NSObject and adopt ASAuthorizationControllerDelegate.

    The continuation is set in the same scope where the request is executed.

    The most important rule is: The continuation must be resumed exactly once

    import AuthenticationServices
    
    class Authorization: NSObject, ASAuthorizationControllerDelegate {
        
        private var continuation : CheckedContinuation<ASAuthorization,Error>?
        
        func signInWithApple() async throws -> ASAuthorization {
            return try await withCheckedThrowingContinuation { continuation in
        
                self.continuation = continuation
                let appleIDProvider = ASAuthorizationAppleIDProvider()
                let request = appleIDProvider.createRequest()
                request.requestedScopes = [.fullName, .email]
                let authorizationController = ASAuthorizationController(authorizationRequests: [request])
                authorizationController.delegate = self
                authorizationController.performRequests()
            }
        }
        
        func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
            continuation?.resume(returning: authorization)
        }
        
        func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
            continuation?.resume(throwing: error)
        }
    }