From 53733af5150213a059ee89e92565d8859291ffa9 Mon Sep 17 00:00:00 2001 From: Yll Fejziu Date: Thu, 2 May 2024 15:07:38 +0200 Subject: [PATCH 1/7] Rewrite AppleAuthenticator to async --- Resources/Apple/README.md | 17 ++----- Sources/Apple/AppleAuthenticator.swift | 67 ++++++++++++-------------- 2 files changed, 35 insertions(+), 49 deletions(-) diff --git a/Resources/Apple/README.md b/Resources/Apple/README.md index 74e9dde..ca804ef 100644 --- a/Resources/Apple/README.md +++ b/Resources/Apple/README.md @@ -20,29 +20,20 @@ Please read [official documentation](https://developer.apple.com/sign-in-with-ap let authenticator = AppleAuthenticator() // conforms to `AppleAuthProvidable` protocol // signIn user -authenticator +let result = try await authenticator .signIn(from: ) - .finally { - // handle result - } // signIn user with nonce -authenticator +let result = try await authenticator .signIn(from: , with: .random(length: 32)) - .finally { - // handle result - } // get authentication status let status = authenticator.isAuthenticated // check authentication status // we should check this when we need to explicitly query authenticator to check if authenticated -authenticator - .checkAuthentication - .finally { - // check result - } +let isAuthenticated = try await authenticator + .checkAuthentication() // signOut user authenticator.signOut() // all provider data regarding the use auth is cleared at this point diff --git a/Sources/Apple/AppleAuthenticator.swift b/Sources/Apple/AppleAuthenticator.swift index a881de2..919c3b4 100644 --- a/Sources/Apple/AppleAuthenticator.swift +++ b/Sources/Apple/AppleAuthenticator.swift @@ -9,15 +9,14 @@ import AuthenticationServices import Foundation import PovioKitAuthCore -import PovioKitPromise public final class AppleAuthenticator: NSObject { private let storage: UserDefaults private let storageUserIdKey = "signIn.userId" private let storageAuthenticatedKey = "authenticated" private let provider: ASAuthorizationAppleIDProvider - private var processingPromise: Promise? - + private var continuation: CheckedContinuation? + public init(storage: UserDefaults? = nil) { self.provider = .init() self.storage = storage ?? .init(suiteName: "povioKit.auth.apple") ?? .standard @@ -34,25 +33,19 @@ public final class AppleAuthenticator: NSObject { extension AppleAuthenticator: Authenticator { /// SignIn user /// - /// Will return promise with the `Response` object on success or with `Error` on error. - public func signIn(from presentingViewController: UIViewController) -> Promise { - let promise = Promise() - processingPromise = promise - appleSignIn(on: presentingViewController, with: nil) - return promise + /// Will asynchronously return the `Response` object on success or with `Error` on error. + public func signIn(from presentingViewController: UIViewController) async throws -> Response { + try await appleSignIn(on: presentingViewController, with: nil) } - + /// SignIn user with `nonce` value /// /// Nonce is usually needed when doing auth with an external auth provider (e.g. firebase). - /// Will return promise with the `Response` object on success or with `Error` on error. - public func signIn(from presentingViewController: UIViewController, with nonce: Nonce) -> Promise { - let promise = Promise() - processingPromise = promise - appleSignIn(on: presentingViewController, with: nonce) - return promise + /// Will asynchronously return the `Response` object on success or with `Error` on error. + public func signIn(from presentingViewController: UIViewController, with nonce: Nonce) async throws -> Response { + try await appleSignIn(on: presentingViewController, with: nonce) } - + /// Clears the signIn footprint and logs out the user immediatelly. public func signOut() { storage.removeObject(forKey: storageUserIdKey) @@ -64,19 +57,19 @@ extension AppleAuthenticator: Authenticator { storage.string(forKey: storageUserIdKey) != nil && storage.bool(forKey: storageAuthenticatedKey) } - /// Checks the current auth state and returns the boolean value as promise. - public var checkAuthentication: Promise { + /// Checks the current auth state and returns the boolean value asynchronously. + public func checkAuthentication() async -> Authenticated { guard let userId = storage.string(forKey: storageUserIdKey) else { - return .value(false) + return false } - - return Promise { seal in + + return await withCheckedContinuation { continuation in ASAuthorizationAppleIDProvider().getCredentialState(forUserID: userId) { credentialsState, _ in - seal.resolve(with: credentialsState == .authorized) + continuation.resume(returning: credentialsState == .authorized) } } } - + /// Boolean if given `url` should be handled. /// /// Call this from UIApplicationDelegate’s `application:openURL:options:` method. @@ -137,7 +130,7 @@ extension AppleAuthenticator: ASAuthorizationControllerDelegate { email: email, expiresAt: expiresAt) - processingPromise?.resolve(with: response) + continuation?.resume(with: .success(response)) case _: rejectSignIn(with: .unhandledAuthorization) } @@ -155,15 +148,14 @@ extension AppleAuthenticator: ASAuthorizationControllerDelegate { // MARK: - Private Methods private extension AppleAuthenticator { - func appleSignIn(on presentingViewController: UIViewController, with nonce: Nonce?) { + func appleSignIn(on presentingViewController: UIViewController, with nonce: Nonce?) async throws -> Response { let request = provider.createRequest() request.requestedScopes = [.fullName, .email] - + switch nonce { case .random(let length): guard length > 0 else { - rejectSignIn(with: .invalidNonceLength) - return + throw Error.invalidNonceLength } request.nonce = generateNonceString(length: length).sha256 case .custom(let value): @@ -171,11 +163,14 @@ private extension AppleAuthenticator { case .none: break } - - let controller = ASAuthorizationController(authorizationRequests: [request]) - controller.delegate = self - controller.presentationContextProvider = presentingViewController - controller.performRequests() + + return try await withCheckedThrowingContinuation { continuation in + let controller = ASAuthorizationController(authorizationRequests: [request]) + controller.delegate = self + controller.presentationContextProvider = presentingViewController + self.continuation = continuation + controller.performRequests() + } } func setupCredentialsRevokeListener() { @@ -187,8 +182,8 @@ private extension AppleAuthenticator { func rejectSignIn(with error: Error) { storage.setValue(false, forKey: storageAuthenticatedKey) - processingPromise?.reject(with: error) - processingPromise = nil + continuation?.resume(throwing: error) + continuation = nil } } From 0fe6fd5891a289710246162c852fcd8c0fdc581b Mon Sep 17 00:00:00 2001 From: Yll Fejziu Date: Thu, 2 May 2024 15:08:02 +0200 Subject: [PATCH 2/7] Rewrite GoogleAuthenticator to async --- Resources/Google/README.md | 5 +- Sources/Google/GoogleAuthenticator.swift | 77 +++++++++++++----------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/Resources/Google/README.md b/Resources/Google/README.md index 725d2d6..5fa50ee 100644 --- a/Resources/Google/README.md +++ b/Resources/Google/README.md @@ -12,11 +12,8 @@ Please read [official documentation](https://developers.google.com/identity/sign let authenticator = GoogleAuthenticator() // signIn user -authenticator +let result = try await authenticator .signIn(from: ) - .finally { - // handle result - } // get authentication status let state = authenticator.isAuthenticated diff --git a/Sources/Google/GoogleAuthenticator.swift b/Sources/Google/GoogleAuthenticator.swift index 467c553..6238960 100644 --- a/Sources/Google/GoogleAuthenticator.swift +++ b/Sources/Google/GoogleAuthenticator.swift @@ -9,7 +9,6 @@ import Foundation import GoogleSignIn import PovioKitAuthCore -import PovioKitPromise public final class GoogleAuthenticator { private let provider: GIDSignIn @@ -23,45 +22,15 @@ public final class GoogleAuthenticator { extension GoogleAuthenticator: Authenticator { /// SignIn user. /// - /// Will return promise with the `Response` object on success or with `Error` on error. + /// Will asynchronously return the `Response` object on success or with `Error` on error. public func signIn(from presentingViewController: UIViewController, hint: String? = .none, - additionalScopes: [String]? = .none) -> Promise { + additionalScopes: [String]? = .none) async throws -> Response { guard !provider.hasPreviousSignIn() else { - // restore user - return Promise { seal in - provider.restorePreviousSignIn { result, error in - switch (result, error) { - case (let user?, _): - seal.resolve(with: user.authResponse) - case (_, let actualError?): - seal.reject(with: Error.system(actualError)) - case (.none, .none): - seal.reject(with: Error.unhandledAuthorization) - } - } - } + return try await restorePreviousSignIn() } - // sign in - return Promise { seal in - provider - .signIn(withPresenting: presentingViewController, hint: hint, additionalScopes: additionalScopes) { result, error in - switch (result, error) { - case (let signInResult?, _): - seal.resolve(with: signInResult.user.authResponse) - case (_, let actualError?): - let errorCode = (actualError as NSError).code - if errorCode == GIDSignInError.Code.canceled.rawValue { - seal.reject(with: Error.cancelled) - } else { - seal.reject(with: Error.system(actualError)) - } - case (.none, .none): - seal.reject(with: Error.unhandledAuthorization) - } - } - } + return try await signInUser(from: presentingViewController, hint: hint, additionalScopes: additionalScopes) } /// Clears the signIn footprint and logs out the user immediatelly. @@ -82,6 +51,44 @@ extension GoogleAuthenticator: Authenticator { } } +// MARK: - Private Methods +private extension GoogleAuthenticator { + func restorePreviousSignIn() async throws -> Response { + try await withCheckedThrowingContinuation { continuation in + provider.restorePreviousSignIn { user, error in + if let user = user { + continuation.resume(returning: user.authResponse) + } else if let error = error { + continuation.resume(throwing: Error.system(error)) + } else { + continuation.resume(throwing: Error.unhandledAuthorization) + } + } + } + } + + func signInUser(from presentingViewController: UIViewController, hint: String?, additionalScopes: [String]?) async throws -> Response { + try await withCheckedThrowingContinuation { continuation in + provider + .signIn(withPresenting: presentingViewController, hint: hint, additionalScopes: additionalScopes) { result, error in + switch (result, error) { + case (let signInResult?, _): + continuation.resume(returning: signInResult.user.authResponse) + case (_, let actualError?): + let errorCode = (actualError as NSError).code + if errorCode == GIDSignInError.Code.canceled.rawValue { + continuation.resume(throwing: Error.cancelled) + } else { + continuation.resume(throwing: Error.system(actualError)) + } + case (.none, .none): + continuation.resume(throwing: Error.unhandledAuthorization) + } + } + } + } +} + // MARK: - Error public extension GoogleAuthenticator { enum Error: Swift.Error { From 1681911317cf24feb8b0d1e3e9fb1c49f22aa3bc Mon Sep 17 00:00:00 2001 From: Yll Fejziu Date: Thu, 2 May 2024 15:08:46 +0200 Subject: [PATCH 3/7] Rewrite FacebookAuthenticator to async --- Resources/Facebook/README.md | 10 +-- Sources/Facebook/FacebookAuthenticator.swift | 73 ++++++++++---------- 2 files changed, 37 insertions(+), 46 deletions(-) diff --git a/Resources/Facebook/README.md b/Resources/Facebook/README.md index 3335fdf..3cb3062 100644 --- a/Resources/Facebook/README.md +++ b/Resources/Facebook/README.md @@ -12,18 +12,12 @@ Please read [official documentation](https://developers.facebook.com/docs/facebo let authenticator = FacebookAuthenticator() // signIn user with default permissions -authenticator +let result = try await authenticator .signIn(from: ) - .finally { - // handle result - } // signIn user with custom permissions -authenticator +let result = try await authenticator .signIn(from: , with: []) - .finally { - // handle result - } // get authentication status let state = authenticator.isAuthenticated diff --git a/Sources/Facebook/FacebookAuthenticator.swift b/Sources/Facebook/FacebookAuthenticator.swift index bc115b2..7863867 100644 --- a/Sources/Facebook/FacebookAuthenticator.swift +++ b/Sources/Facebook/FacebookAuthenticator.swift @@ -10,7 +10,6 @@ import Foundation import FacebookLogin import PovioKitCore import PovioKitAuthCore -import PovioKitPromise public final class FacebookAuthenticator { private let provider: LoginManager @@ -25,15 +24,14 @@ extension FacebookAuthenticator: Authenticator { /// SignIn user. /// /// The `permissions` to use when doing a sign in. - /// Will return promise with the `Response` object on success or with `Error` on error. + /// Will asynchronously return the `Response` object on success or with `Error` on error. public func signIn( from presentingViewController: UIViewController, - with permissions: [Permission] = [.email, .publicProfile]) -> Promise + with permissions: [Permission] = [.email, .publicProfile]) async throws -> Response { let permissions: [String] = permissions.map { $0.name } - - return signIn(with: permissions, on: presentingViewController) - .flatMap(with: fetchUserDetails) + let token = try await signIn(with: permissions, on: presentingViewController) + return try await fetchUserDetails(with: token) } /// Clears the signIn footprint and logs out the user immediatelly. @@ -69,38 +67,37 @@ public extension FacebookAuthenticator { // MARK: - Private Methods private extension FacebookAuthenticator { - func signIn(with permissions: [String], on presentingViewController: UIViewController) -> Promise { - Promise { seal in - provider - .logIn(permissions: permissions, from: presentingViewController) { result, error in - switch (result, error) { - case (let result?, nil): - if result.isCancelled { - seal.reject(with: Error.cancelled) - } else if let token = result.token { - seal.resolve(with: token) - } else { - seal.reject(with: Error.invalidIdentityToken) - } - case (nil, let error?): - seal.reject(with: Error.system(error)) - case _: - seal.reject(with: Error.system(NSError(domain: "com.povio.facebook.error", code: -1, userInfo: nil))) + func signIn(with permissions: [String], on presentingViewController: UIViewController) async throws -> AccessToken { + try await withCheckedThrowingContinuation { continuation in + provider.logIn(permissions: permissions, from: presentingViewController) { result, error in + switch (result, error) { + case (let result?, nil): + if result.isCancelled { + continuation.resume(throwing: Error.cancelled) + } else if let token = result.token { + continuation.resume(returning: token) + } else { + continuation.resume(throwing: Error.invalidIdentityToken) } + case (nil, let error?): + continuation.resume(throwing: Error.system(error)) + default: + continuation.resume(throwing: Error.system(NSError(domain: "com.povio.facebook.error", code: -1, userInfo: nil))) } + } } } - - func fetchUserDetails(with token: AccessToken) -> Promise { - let request = GraphRequest( - graphPath: "me", - parameters: ["fields": "id, email, first_name, last_name"], - tokenString: token.tokenString, - httpMethod: nil, - flags: .doNotInvalidateTokenOnError - ) - - return Promise { seal in + + func fetchUserDetails(with token: AccessToken) async throws -> Response { + try await withCheckedThrowingContinuation { continuation in + let request = GraphRequest( + graphPath: "me", + parameters: ["fields": "id, email, first_name, last_name"], + tokenString: token.tokenString, + httpMethod: nil, + flags: .doNotInvalidateTokenOnError + ) + request.start { _, result, error in switch result { case .some(let response): @@ -110,7 +107,7 @@ private extension FacebookAuthenticator { do { let data = try JSONSerialization.data(withJSONObject: response, options: []) let object = try data.decode(GraphResponse.self, with: decoder) - + let authResponse = Response( userId: object.id, token: token.tokenString, @@ -118,12 +115,12 @@ private extension FacebookAuthenticator { email: object.email, expiresAt: token.expirationDate ) - seal.resolve(with: authResponse) + continuation.resume(returning: authResponse) } catch { - seal.reject(with: Error.userDataDecode) + continuation.resume(throwing: Error.userDataDecode) } case .none: - seal.reject(with: Error.missingUserData) + continuation.resume(throwing: Error.missingUserData) } } } From fe280cec4e45996166bf530578a4049e48ba20e8 Mon Sep 17 00:00:00 2001 From: Yll Fejziu Date: Thu, 2 May 2024 15:36:41 +0200 Subject: [PATCH 4/7] Update docs --- README.md | 18 ++++++------------ Sources/Apple/AppleAuthenticator.swift | 4 ++-- Sources/Facebook/FacebookAuthenticator.swift | 2 +- Sources/Google/GoogleAuthenticator.swift | 2 +- Sources/LinkedIn/LinkedInAuthenticator.swift | 2 +- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 2009216..4ac93f9 100644 --- a/README.md +++ b/README.md @@ -54,19 +54,13 @@ let manager = SocialAuthenticationManager(authenticators: [AppleAuthenticator(), // signIn user with Apple let appleAuthenticator = manager.authenticator(for: AppleAuthenticator.self) -appleAuthenticator? +let result = try await appleAuthenticator? .signIn(from: , with: .random(length: 32)) - .finally { - // handle result - } // signIn user with Facebook let facebookAuthenticator = manager.authenticator(for: FacebookAuthenticator.self) -facebookAuthenticator? +let result = try await facebookAuthenticator? .signIn(from: , with: [.email]) - .finally { - // handle result - } // return currently authenticated authenticator let authenticated: Authenticator? = manager.authenticator @@ -123,11 +117,11 @@ You can easily add new authenticator that is not built-in with PovioKitAuth pack ```swift final class SnapchatAuthenticator: Authenticator { - public func signIn(from presentingViewController: UIViewController) -> Promise { - Promise { seal in + public func signIn(from presentingViewController: UIViewController) async throws -> Response { + try await withCheckedThrowingContinuation { continuation in SCSDKLoginClient.login(from: presentingViewController) { [weak self] success, error in guard success, error == nil else { - seal.reject(with: error) + continuation.resume(throwing: error) return } @@ -135,7 +129,7 @@ final class SnapchatAuthenticator: Authenticator { let variables = ["page": "bitmoji"] SCSDKLoginClient.fetchUserData(withQuery: query, variables: variables) { resources in ... - seal.resolve(with: response) + continuation.resume(returning: response) } } } diff --git a/Sources/Apple/AppleAuthenticator.swift b/Sources/Apple/AppleAuthenticator.swift index 919c3b4..9f03e7e 100644 --- a/Sources/Apple/AppleAuthenticator.swift +++ b/Sources/Apple/AppleAuthenticator.swift @@ -33,7 +33,7 @@ public final class AppleAuthenticator: NSObject { extension AppleAuthenticator: Authenticator { /// SignIn user /// - /// Will asynchronously return the `Response` object on success or with `Error` on error. + /// Will asynchronously return the `Response` object on success or `Error` on error. public func signIn(from presentingViewController: UIViewController) async throws -> Response { try await appleSignIn(on: presentingViewController, with: nil) } @@ -41,7 +41,7 @@ extension AppleAuthenticator: Authenticator { /// SignIn user with `nonce` value /// /// Nonce is usually needed when doing auth with an external auth provider (e.g. firebase). - /// Will asynchronously return the `Response` object on success or with `Error` on error. + /// Will asynchronously return the `Response` object on success or `Error` on error. public func signIn(from presentingViewController: UIViewController, with nonce: Nonce) async throws -> Response { try await appleSignIn(on: presentingViewController, with: nonce) } diff --git a/Sources/Facebook/FacebookAuthenticator.swift b/Sources/Facebook/FacebookAuthenticator.swift index 7863867..d811bd4 100644 --- a/Sources/Facebook/FacebookAuthenticator.swift +++ b/Sources/Facebook/FacebookAuthenticator.swift @@ -24,7 +24,7 @@ extension FacebookAuthenticator: Authenticator { /// SignIn user. /// /// The `permissions` to use when doing a sign in. - /// Will asynchronously return the `Response` object on success or with `Error` on error. + /// Will asynchronously return the `Response` object on success or `Error` on error. public func signIn( from presentingViewController: UIViewController, with permissions: [Permission] = [.email, .publicProfile]) async throws -> Response diff --git a/Sources/Google/GoogleAuthenticator.swift b/Sources/Google/GoogleAuthenticator.swift index 6238960..6e7f88d 100644 --- a/Sources/Google/GoogleAuthenticator.swift +++ b/Sources/Google/GoogleAuthenticator.swift @@ -22,7 +22,7 @@ public final class GoogleAuthenticator { extension GoogleAuthenticator: Authenticator { /// SignIn user. /// - /// Will asynchronously return the `Response` object on success or with `Error` on error. + /// Will asynchronously return the `Response` object on success or `Error` on error. public func signIn(from presentingViewController: UIViewController, hint: String? = .none, additionalScopes: [String]? = .none) async throws -> Response { diff --git a/Sources/LinkedIn/LinkedInAuthenticator.swift b/Sources/LinkedIn/LinkedInAuthenticator.swift index 79155d3..bc91c4d 100644 --- a/Sources/LinkedIn/LinkedInAuthenticator.swift +++ b/Sources/LinkedIn/LinkedInAuthenticator.swift @@ -25,7 +25,7 @@ public final class LinkedInAuthenticator { extension LinkedInAuthenticator: Authenticator { /// SignIn user. /// - /// Will return promise with the `Response` object on success or with `Error` on error. + /// Will asynchronously return the `Response` object on success or `Error` on error. public func signIn(authCode: String, configuration: Configuration) async throws -> Response { let authRequest: LinkedInAPI.LinkedInAuthRequest = .init( code: authCode, From 89f239bda5447ae3fb8893e6a057c19879bc257d Mon Sep 17 00:00:00 2001 From: Yll Fejziu Date: Fri, 10 May 2024 13:00:27 +0200 Subject: [PATCH 5/7] Fix Apple sign out --- Sources/Apple/AppleAuthenticator.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Apple/AppleAuthenticator.swift b/Sources/Apple/AppleAuthenticator.swift index 9f03e7e..1b2f454 100644 --- a/Sources/Apple/AppleAuthenticator.swift +++ b/Sources/Apple/AppleAuthenticator.swift @@ -49,7 +49,8 @@ extension AppleAuthenticator: Authenticator { /// Clears the signIn footprint and logs out the user immediatelly. public func signOut() { storage.removeObject(forKey: storageUserIdKey) - rejectSignIn(with: .cancelled) + storage.setValue(false, forKey: storageAuthenticatedKey) + continuation = nil } /// Returns the current authentication state. From 14ef45e0e5e26604b3af176e749db92911c4a69e Mon Sep 17 00:00:00 2001 From: Yll Fejziu Date: Tue, 14 May 2024 13:49:30 +0200 Subject: [PATCH 6/7] Address comment --- Sources/Apple/AppleAuthenticator.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Sources/Apple/AppleAuthenticator.swift b/Sources/Apple/AppleAuthenticator.swift index 1b2f454..cb407a7 100644 --- a/Sources/Apple/AppleAuthenticator.swift +++ b/Sources/Apple/AppleAuthenticator.swift @@ -64,11 +64,8 @@ extension AppleAuthenticator: Authenticator { return false } - return await withCheckedContinuation { continuation in - ASAuthorizationAppleIDProvider().getCredentialState(forUserID: userId) { credentialsState, _ in - continuation.resume(returning: credentialsState == .authorized) - } - } + let credentialState = try? await ASAuthorizationAppleIDProvider().credentialState(forUserID: userId) + return credentialState == .authorized } /// Boolean if given `url` should be handled. From 986a211a7fa14c61a0da5028ea111c8cc963d471 Mon Sep 17 00:00:00 2001 From: Yll Fejziu Date: Wed, 15 May 2024 13:50:36 +0200 Subject: [PATCH 7/7] Remove checkAuthentication method (Apple) --- Resources/Apple/README.md | 5 ----- Sources/Apple/AppleAuthenticator.swift | 10 ---------- 2 files changed, 15 deletions(-) diff --git a/Resources/Apple/README.md b/Resources/Apple/README.md index ca804ef..c701d5a 100644 --- a/Resources/Apple/README.md +++ b/Resources/Apple/README.md @@ -30,11 +30,6 @@ let result = try await authenticator // get authentication status let status = authenticator.isAuthenticated -// check authentication status -// we should check this when we need to explicitly query authenticator to check if authenticated -let isAuthenticated = try await authenticator - .checkAuthentication() - // signOut user authenticator.signOut() // all provider data regarding the use auth is cleared at this point ``` diff --git a/Sources/Apple/AppleAuthenticator.swift b/Sources/Apple/AppleAuthenticator.swift index cb407a7..37901de 100644 --- a/Sources/Apple/AppleAuthenticator.swift +++ b/Sources/Apple/AppleAuthenticator.swift @@ -57,16 +57,6 @@ extension AppleAuthenticator: Authenticator { public var isAuthenticated: Authenticated { storage.string(forKey: storageUserIdKey) != nil && storage.bool(forKey: storageAuthenticatedKey) } - - /// Checks the current auth state and returns the boolean value asynchronously. - public func checkAuthentication() async -> Authenticated { - guard let userId = storage.string(forKey: storageUserIdKey) else { - return false - } - - let credentialState = try? await ASAuthorizationAppleIDProvider().credentialState(forUserID: userId) - return credentialState == .authorized - } /// Boolean if given `url` should be handled. ///