Convert importOPML to async await.

This commit is contained in:
Brent Simmons 2024-04-22 22:53:57 -07:00
parent 6b1e41b694
commit d912bcbe79
3 changed files with 32 additions and 72 deletions

View File

@ -48,10 +48,10 @@ let package = Package(
"FeedFinder", "FeedFinder",
"CommonErrors", "CommonErrors",
"Feedly" "Feedly"
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
] ]
// swiftSettings: [
// .enableExperimentalFeature("StrictConcurrency")
// ]
), ),
.testTarget( .testTarget(
name: "AccountTests", name: "AccountTests",

View File

@ -259,56 +259,27 @@ final class FeedlyAccountDelegate: AccountDelegate {
func importOPML(for account: Account, opmlFile: URL) async throws { func importOPML(for account: Account, opmlFile: URL) async throws {
try await withCheckedThrowingContinuation { continuation in let data = try Data(contentsOf: opmlFile)
self.importOPML(for: account, opmlFile: opmlFile) { result in
switch result { os_log(.debug, log: log, "Begin importing OPML…")
case .success:
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
private func importOPML(for account: Account, opmlFile: URL, completion: @escaping @Sendable (Result<Void, Error>) -> 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 isOPMLImportInProgress = true
refreshProgress.addToNumberOfTasksAndRemaining(1) refreshProgress.addTask()
defer {
caller.importOpml(data) { result in isOPMLImportInProgress = false
refreshProgress.completeTask()
}
MainActor.assumeIsolated { do {
switch result { try await caller.importOPML(data)
case .success: os_log(.debug, log: self.log, "Import OPML done.")
os_log(.debug, log: self.log, "Import OPML done.") } catch {
self.refreshProgress.completeTask() os_log(.debug, log: self.log, "Import OPML failed.")
self.isOPMLImportInProgress = false let wrappedError = AccountError.wrappedError(error: error, account: account)
DispatchQueue.main.async { throw wrappedError
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))
}
}
}
} }
} }
func createFolder(for account: Account, name: String) async throws -> Folder { func createFolder(for account: Account, name: String) async throws -> Folder {
try await withCheckedThrowingContinuation { continuation in try await withCheckedThrowingContinuation { continuation in

View File

@ -17,8 +17,8 @@ protocol FeedlyAPICallerDelegate: AnyObject {
@MainActor func reauthorizeFeedlyAPICaller(_ caller: FeedlyAPICaller, completionHandler: @escaping (Bool) -> ()) @MainActor func reauthorizeFeedlyAPICaller(_ caller: FeedlyAPICaller, completionHandler: @escaping (Bool) -> ())
} }
final class FeedlyAPICaller { @MainActor final class FeedlyAPICaller {
enum API { enum API {
case sandbox case sandbox
case cloud case cloud
@ -151,43 +151,32 @@ final class FeedlyAPICaller {
} }
} }
func importOpml(_ opmlData: Data, completion: @escaping @Sendable (Result<Void, Error>) -> ()) { func importOPML(_ opmlData: Data) async throws {
guard !isSuspended else { guard !isSuspended else {
return DispatchQueue.main.async { throw TransportError.suspended
completion(.failure(TransportError.suspended))
}
} }
guard let accessToken = credentials?.secret else { guard let accessToken = credentials?.secret else {
return DispatchQueue.main.async { throw CredentialsError.incompleteCredentials
completion(.failure(CredentialsError.incompleteCredentials))
}
} }
var components = baseUrlComponents var components = baseUrlComponents
components.path = "/v3/opml" components.path = "/v3/opml"
guard let url = components.url else { guard let url = components.url else {
fatalError("\(components) does not produce a valid URL.") fatalError("\(components) does not produce a valid URL.")
} }
var request = URLRequest(url: url) var request = URLRequest(url: url)
request.httpMethod = "POST" request.httpMethod = "POST"
request.addValue("text/xml", forHTTPHeaderField: HTTPRequestHeader.contentType) request.addValue("text/xml", forHTTPHeaderField: HTTPRequestHeader.contentType)
request.addValue("application/json", forHTTPHeaderField: "Accept-Type") request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization) request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
request.httpBody = opmlData request.httpBody = opmlData
send(request: request, resultType: String.self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in let (httpResponse, _) = try await send(request: request, resultType: String.self)
switch result { if httpResponse.statusCode != HTTPResponseCode.OK {
case .success(let (httpResponse, _)): throw URLError(.cannotDecodeContentData)
if httpResponse.statusCode == 200 {
completion(.success(()))
} else {
completion(.failure(URLError(.cannotDecodeContentData)))
}
case .failure(let error):
completion(.failure(error))
}
} }
} }