From ad151b5fa4d20b3b8e6f57d92c16505927876db3 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Fri, 5 Apr 2024 18:16:52 -0700 Subject: [PATCH] Convert the last completion-based API in Account to async await. --- Account/Sources/Account/Account.swift | 5 +-- .../Feedly/FeedlyAccountDelegate+OAuth.swift | 42 ++++++++++--------- .../OAuthAccountAuthorizationOperation.swift | 27 ++++-------- .../OAuthAuthorizationCodeGranting.swift | 6 +-- 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/Account/Sources/Account/Account.swift b/Account/Sources/Account/Account.swift index 3fe0c5fb6..80257ebae 100644 --- a/Account/Sources/Account/Account.swift +++ b/Account/Sources/Account/Account.swift @@ -400,8 +400,7 @@ public enum FetchType { client: OAuthAuthorizationClient, accountType: AccountType, transport: Transport = URLSession.webserviceTransport(), - secretsProvider: SecretsProvider, - completion: @escaping (Result) -> ()) { + secretsProvider: SecretsProvider) async throws -> OAuthAuthorizationGrant { let grantingType: OAuthAuthorizationGranting.Type switch accountType { @@ -411,7 +410,7 @@ public enum FetchType { fatalError("\(accountType) does not support OAuth authorization code granting.") } - grantingType.requestOAuthAccessToken(with: response, transport: transport, secretsProvider: secretsProvider, completion: completion) + return try await grantingType.requestOAuthAccessToken(with: response, transport: transport, secretsProvider: secretsProvider) } public func receiveRemoteNotification(userInfo: [AnyHashable: Any]) async { diff --git a/Account/Sources/Account/Feedly/FeedlyAccountDelegate+OAuth.swift b/Account/Sources/Account/Feedly/FeedlyAccountDelegate+OAuth.swift index 814ca074c..012b1f87f 100644 --- a/Account/Sources/Account/Feedly/FeedlyAccountDelegate+OAuth.swift +++ b/Account/Sources/Account/Feedly/FeedlyAccountDelegate+OAuth.swift @@ -38,30 +38,34 @@ extension FeedlyAccountDelegate: OAuthAuthorizationGranting { return FeedlyAPICaller.authorizationCodeUrlRequest(for: authorizationRequest, baseUrlComponents: baseURLComponents) } - static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: Transport, secretsProvider: SecretsProvider, completion: @escaping (Result) -> ()) { + static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: any Web.Transport, secretsProvider: any Secrets.SecretsProvider) async throws -> OAuthAuthorizationGrant { + let client = environment.oauthAuthorizationClient(secretsProvider: secretsProvider) let request = OAuthAccessTokenRequest(authorizationResponse: response, scope: oauthAuthorizationGrantScope, client: client) let caller = FeedlyAPICaller(transport: transport, api: environment, secretsProvider: secretsProvider) - caller.requestAccessToken(request) { result in - switch result { - case .success(let response): - let accessToken = Credentials(type: .oauthAccessToken, username: response.id, secret: response.accessToken) - - let refreshToken: Credentials? = { - guard let token = response.refreshToken else { - return nil - } - return Credentials(type: .oauthRefreshToken, username: response.id, secret: token) - }() - - let grant = OAuthAuthorizationGrant(accessToken: accessToken, refreshToken: refreshToken) - - completion(.success(grant)) - - case .failure(let error): - completion(.failure(error)) + + return try await withCheckedThrowingContinuation { continuation in + caller.requestAccessToken(request) { result in + switch result { + case .success(let response): + let accessToken = Credentials(type: .oauthAccessToken, username: response.id, secret: response.accessToken) + + let refreshToken: Credentials? = { + guard let token = response.refreshToken else { + return nil + } + return Credentials(type: .oauthRefreshToken, username: response.id, secret: token) + }() + + let grant = OAuthAuthorizationGrant(accessToken: accessToken, refreshToken: refreshToken) + + continuation.resume(returning: grant) + + case .failure(let error): + continuation.resume(throwing: error) + } } } } diff --git a/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift b/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift index b5dcd9831..05903db32 100644 --- a/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift +++ b/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift @@ -101,7 +101,8 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError { } private func didEndAuthentication(url: URL?, error: Error?) { - MainActor.assumeIsolated { + + Task { @MainActor in guard !isCanceled else { didFinish() return @@ -109,15 +110,16 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError { do { guard let url = url else { - if let error = error { + if let error { throw error } throw URLError(.badURL) } - let response = try OAuthAuthorizationResponse(url: url, client: oauthClient) + let response = try OAuthAuthorizationResponse(url: url, client: self.oauthClient) - Account.requestOAuthAccessToken(with: response, client: oauthClient, accountType: accountType, secretsProvider: secretsProvider, completion: didEndRequestingAccessToken(_:)) + let tokenResponse = try await Account.requestOAuthAccessToken(with: response, client: oauthClient, accountType: accountType, secretsProvider: secretsProvider) + saveAccount(for: tokenResponse) } catch is ASWebAuthenticationSessionError { didFinish() // Primarily, cancellation. @@ -127,7 +129,8 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError { } } } - + + public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { guard let anchor = presentationAnchor else { fatalError("\(self) has outlived presentation anchor.") @@ -135,20 +138,6 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError { return anchor } - @MainActor private func didEndRequestingAccessToken(_ result: Result) { - guard !isCanceled else { - didFinish() - return - } - - switch result { - case .success(let tokenResponse): - saveAccount(for: tokenResponse) - case .failure(let error): - didFinish(error) - } - } - @MainActor private func saveAccount(for grant: OAuthAuthorizationGrant) { guard !AccountManager.shared.duplicateServiceAccount(type: .feedly, username: grant.accessToken.username) else { didFinish(OAuthAccountAuthorizationOperationError.duplicateAccount) diff --git a/Account/Sources/Account/Feedly/OAuthAuthorizationCodeGranting.swift b/Account/Sources/Account/Feedly/OAuthAuthorizationCodeGranting.swift index 3d276ae97..31242adbb 100644 --- a/Account/Sources/Account/Feedly/OAuthAuthorizationCodeGranting.swift +++ b/Account/Sources/Account/Feedly/OAuthAuthorizationCodeGranting.swift @@ -167,7 +167,7 @@ public protocol OAuthAuthorizationCodeGrantRequesting { protocol OAuthAuthorizationGranting: AccountDelegate { - static func oauthAuthorizationCodeGrantRequest(secretsProvider: SecretsProvider) -> URLRequest - - static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: Transport, secretsProvider: SecretsProvider, completion: @escaping (Result) -> ()) + @MainActor static func oauthAuthorizationCodeGrantRequest(secretsProvider: SecretsProvider) -> URLRequest + + @MainActor static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: Transport, secretsProvider: SecretsProvider) async throws -> OAuthAuthorizationGrant }