diff --git a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift index 33b733f0e..045b882e3 100644 --- a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift +++ b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift @@ -128,11 +128,11 @@ final class ReaderAPIAccountDelegate: AccountDelegate { self.refreshProgress.completeTask() self.caller.retrieveItemIDs(type: .allForAccount) { result in self.refreshProgress.completeTask() - + switch result { - + case .success(let articleIDs): - + Task { @MainActor in try? await account.markAsRead(Set(articleIDs)) self.refreshArticleStatus(for: account) { _ in @@ -145,40 +145,33 @@ final class ReaderAPIAccountDelegate: AccountDelegate { } } } - + case .failure(let error): completion(.failure(error)) } } } - + case .failure(let error): - DispatchQueue.main.async { + + Task { @MainActor in self.refreshProgress.clear() let wrappedError = AccountError.wrappedError(error: error, account: account) if wrappedError.isCredentialsError, let basicCredentials = try? account.retrieveCredentials(type: .readerBasic), let endpoint = account.endpointURL { self.caller.credentials = basicCredentials - self.caller.validateCredentials(endpoint: endpoint) { result in - switch result { - case .success(let apiCredentials): - if let apiCredentials = apiCredentials { - DispatchQueue.main.async { - try? account.storeCredentials(apiCredentials) - self.caller.credentials = apiCredentials - self.refreshAll(for: account, completion: completion) - } - } else { - DispatchQueue.main.async { - completion(.failure(wrappedError)) - } - } - case .failure: - DispatchQueue.main.async { - completion(.failure(wrappedError)) - } + do { + if let apiCredentials = try await self.caller.validateCredentials(endpoint: endpoint) { + try? account.storeCredentials(apiCredentials) + self.caller.credentials = apiCredentials + self.refreshAll(for: account, completion: completion) } + else { + completion(.failure(wrappedError)) + } + } catch { + completion(.failure(wrappedError)) } } else { @@ -186,9 +179,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate { } } } - } - } func syncArticleStatus(for account: Account) async throws { @@ -838,35 +829,15 @@ final class ReaderAPIAccountDelegate: AccountDelegate { } static func validateCredentials(transport: Transport, credentials: Credentials, endpoint: URL?, secretsProvider: SecretsProvider) async throws -> Credentials? { - - try await withCheckedThrowingContinuation { continuation in - - self.validateCredentials(transport: transport, credentials: credentials, endpoint: endpoint, secretsProvider: secretsProvider) { result in - - switch result { - case .success(let credentials): - continuation.resume(returning: credentials) - case .failure(let error): - continuation.resume(throwing: error) - } - } - } - } - - private static func validateCredentials(transport: Transport, credentials: Credentials, endpoint: URL?, secretsProvider: SecretsProvider, completion: @escaping (Result) -> Void) { - guard let endpoint = endpoint else { - completion(.failure(TransportError.noURL)) - return + + guard let endpoint else { + throw TransportError.noURL } let caller = ReaderAPICaller(transport: transport, secretsProvider: secretsProvider) caller.credentials = credentials - caller.validateCredentials(endpoint: endpoint) { result in - DispatchQueue.main.async { - completion(result) - } - } + return try await caller.validateCredentials(endpoint: endpoint) } // MARK: Suspend and Resume (for iOS) diff --git a/Account/Sources/Account/ReaderAPI/ReaderAPICaller.swift b/Account/Sources/Account/ReaderAPI/ReaderAPICaller.swift index 7ce22c650..cbcf41230 100644 --- a/Account/Sources/Account/ReaderAPI/ReaderAPICaller.swift +++ b/Account/Sources/Account/ReaderAPI/ReaderAPICaller.swift @@ -15,7 +15,7 @@ enum CreateReaderAPISubscriptionResult { case notFound } -final class ReaderAPICaller: NSObject { +@MainActor final class ReaderAPICaller: NSObject { enum ItemIDType { case unread @@ -93,55 +93,51 @@ final class ReaderAPICaller: NSObject { transport.cancelAll() } - func validateCredentials(endpoint: URL, completion: @escaping (Result) -> Void) { - guard let credentials = credentials else { - completion(.failure(CredentialsError.incompleteCredentials)) - return + func validateCredentials(endpoint: URL) async throws -> Credentials? { + + guard let credentials else { + throw CredentialsError.incompleteCredentials } - + var request = URLRequest(url: endpoint.appendingPathComponent(ReaderAPIEndpoints.login.rawValue), credentials: credentials) addVariantHeaders(&request) - transport.send(request: request) { result in - switch result { - case .success(let (_, data)): - guard let resultData = data else { - completion(.failure(TransportError.noData)) - break - } - - // Convert the return data to UTF8 and then parse out the Auth token - guard let rawData = String(data: resultData, encoding: .utf8) else { - completion(.failure(TransportError.noData)) - break - } - - var authData: [String: String] = [:] - rawData.split(separator: "\n").forEach({ (line: Substring) in - let items = line.split(separator: "=").map{String($0)} - if items.count == 2 { - authData[items[0]] = items[1] - } - }) - - guard let authString = authData["Auth"] else { - completion(.failure(CredentialsError.incompleteCredentials)) - break - } - - // Save Auth Token for later use - self.credentials = Credentials(type: .readerAPIKey, username: credentials.username, secret: authString) - - completion(.success(self.credentials)) - case .failure(let error): - if let transportError = error as? TransportError, case .httpError(let code) = transportError, code == 404 { - completion(.failure(ReaderAPIAccountDelegateError.urlNotFound)) - } else { - completion(.failure(error)) + do { + let (_, data) = try await transport.send(request: request) + + guard let data else { + throw TransportError.noData + } + + // Convert the return data to UTF8 and then parse out the Auth token + guard let rawData = String(data: data, encoding: .utf8) else { + throw TransportError.noData + } + + var authData: [String: String] = [:] + rawData.split(separator: "\n").forEach({ (line: Substring) in + let items = line.split(separator: "=").map{String($0)} + if items.count == 2 { + authData[items[0]] = items[1] } + }) + + guard let authString = authData["Auth"] else { + throw CredentialsError.incompleteCredentials + } + + // Save Auth Token for later use + self.credentials = Credentials(type: .readerAPIKey, username: credentials.username, secret: authString) + + return self.credentials + + } catch { + if let transportError = error as? TransportError, case .httpError(let code) = transportError, code == 404 { + throw ReaderAPIAccountDelegateError.urlNotFound + } else { + throw error } } - } func requestAuthorizationToken(endpoint: URL, completion: @escaping (Result) -> Void) {