From d912bcbe79a504d4067cc22a6a075c30bf645ab8 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Mon, 22 Apr 2024 22:53:57 -0700 Subject: [PATCH] Convert importOPML to async await. --- Account/Package.swift | 6 +- .../FeedlyAccountDelegate.swift | 61 +++++-------------- .../Account/Feedly/FeedlyAPICaller.swift | 37 ++++------- 3 files changed, 32 insertions(+), 72 deletions(-) diff --git a/Account/Package.swift b/Account/Package.swift index 8382fd9b0..cfed8db07 100644 --- a/Account/Package.swift +++ b/Account/Package.swift @@ -48,10 +48,10 @@ let package = Package( "FeedFinder", "CommonErrors", "Feedly" + ], + swiftSettings: [ + .enableExperimentalFeature("StrictConcurrency") ] -// swiftSettings: [ -// .enableExperimentalFeature("StrictConcurrency") -// ] ), .testTarget( name: "AccountTests", diff --git a/Account/Sources/Account/AccountDelegates/FeedlyAccountDelegate.swift b/Account/Sources/Account/AccountDelegates/FeedlyAccountDelegate.swift index e57eacf3d..ba68bfb9f 100644 --- a/Account/Sources/Account/AccountDelegates/FeedlyAccountDelegate.swift +++ b/Account/Sources/Account/AccountDelegates/FeedlyAccountDelegate.swift @@ -259,56 +259,27 @@ final class FeedlyAccountDelegate: AccountDelegate { func importOPML(for account: Account, opmlFile: URL) async throws { - try await withCheckedThrowingContinuation { continuation in - self.importOPML(for: account, opmlFile: opmlFile) { result in - switch result { - case .success: - continuation.resume() - case .failure(let error): - continuation.resume(throwing: error) - } - } - } - } + let data = try Data(contentsOf: opmlFile) + + os_log(.debug, log: log, "Begin importing OPML…") - private func importOPML(for account: Account, opmlFile: URL, completion: @escaping @Sendable (Result) -> Void) { - let data: Data - - do { - data = try Data(contentsOf: opmlFile) - } catch { - completion(.failure(error)) - return - } - - os_log(.debug, log: log, "Begin importing OPML...") isOPMLImportInProgress = true - refreshProgress.addToNumberOfTasksAndRemaining(1) - - caller.importOpml(data) { result in + refreshProgress.addTask() + defer { + isOPMLImportInProgress = false + refreshProgress.completeTask() + } - MainActor.assumeIsolated { - switch result { - case .success: - os_log(.debug, log: self.log, "Import OPML done.") - self.refreshProgress.completeTask() - self.isOPMLImportInProgress = false - DispatchQueue.main.async { - completion(.success(())) - } - case .failure(let error): - os_log(.debug, log: self.log, "Import OPML failed.") - self.refreshProgress.completeTask() - self.isOPMLImportInProgress = false - DispatchQueue.main.async { - let wrappedError = AccountError.wrappedError(error: error, account: account) - completion(.failure(wrappedError)) - } - } - } + do { + try await caller.importOPML(data) + os_log(.debug, log: self.log, "Import OPML done.") + } catch { + os_log(.debug, log: self.log, "Import OPML failed.") + let wrappedError = AccountError.wrappedError(error: error, account: account) + throw wrappedError } } - + func createFolder(for account: Account, name: String) async throws -> Folder { try await withCheckedThrowingContinuation { continuation in diff --git a/Account/Sources/Account/Feedly/FeedlyAPICaller.swift b/Account/Sources/Account/Feedly/FeedlyAPICaller.swift index 80f787595..8729e1606 100644 --- a/Account/Sources/Account/Feedly/FeedlyAPICaller.swift +++ b/Account/Sources/Account/Feedly/FeedlyAPICaller.swift @@ -17,8 +17,8 @@ protocol FeedlyAPICallerDelegate: AnyObject { @MainActor func reauthorizeFeedlyAPICaller(_ caller: FeedlyAPICaller, completionHandler: @escaping (Bool) -> ()) } -final class FeedlyAPICaller { - +@MainActor final class FeedlyAPICaller { + enum API { case sandbox case cloud @@ -151,43 +151,32 @@ final class FeedlyAPICaller { } } - func importOpml(_ opmlData: Data, completion: @escaping @Sendable (Result) -> ()) { + func importOPML(_ opmlData: Data) async throws { + guard !isSuspended else { - return DispatchQueue.main.async { - completion(.failure(TransportError.suspended)) - } + throw TransportError.suspended } - guard let accessToken = credentials?.secret else { - return DispatchQueue.main.async { - completion(.failure(CredentialsError.incompleteCredentials)) - } + throw CredentialsError.incompleteCredentials } + var components = baseUrlComponents components.path = "/v3/opml" - + guard let url = components.url else { fatalError("\(components) does not produce a valid URL.") } - + var request = URLRequest(url: url) request.httpMethod = "POST" request.addValue("text/xml", forHTTPHeaderField: HTTPRequestHeader.contentType) request.addValue("application/json", forHTTPHeaderField: "Accept-Type") request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization) request.httpBody = opmlData - - send(request: request, resultType: String.self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in - switch result { - case .success(let (httpResponse, _)): - if httpResponse.statusCode == 200 { - completion(.success(())) - } else { - completion(.failure(URLError(.cannotDecodeContentData))) - } - case .failure(let error): - completion(.failure(error)) - } + + let (httpResponse, _) = try await send(request: request, resultType: String.self) + if httpResponse.statusCode != HTTPResponseCode.OK { + throw URLError(.cannotDecodeContentData) } }