diff --git a/Account/Sources/Account/Feedly/FeedlyAPICaller.swift b/Account/Sources/Account/Feedly/FeedlyAPICaller.swift index fdda966a2..875231d19 100644 --- a/Account/Sources/Account/Feedly/FeedlyAPICaller.swift +++ b/Account/Sources/Account/Feedly/FeedlyAPICaller.swift @@ -475,56 +475,21 @@ extension FeedlyAPICaller: FeedlyGetStreamIDsService { extension FeedlyAPICaller: FeedlyGetEntriesService { - func getEntries(for ids: Set, completion: @escaping (Result<[FeedlyEntry], Error>) -> ()) { - guard !isSuspended else { - return DispatchQueue.main.async { - completion(.failure(TransportError.suspended)) - } - } - - guard let accessToken = credentials?.secret else { - return DispatchQueue.main.async { - completion(.failure(CredentialsError.incompleteCredentials)) - } - } - - var components = baseURLComponents - components.path = "/v3/entries/.mget" - - guard let url = components.url else { - fatalError("\(components) does not produce a valid URL.") - } - - var request = URLRequest(url: url) - - do { - let body = Array(ids) - let encoder = JSONEncoder() - let data = try encoder.encode(body) - request.httpBody = data - } catch { - return DispatchQueue.main.async { - completion(.failure(error)) - } - } - - request.httpMethod = "POST" - request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType) - request.addValue("application/json", forHTTPHeaderField: "Accept-Type") - request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization) - - send(request: request, resultType: [FeedlyEntry].self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in - switch result { - case .success(let (_, entries)): - if let response = entries { - completion(.success(response)) - } else { - completion(.failure(URLError(.cannotDecodeContentData))) - } - case .failure(let error): - completion(.failure(error)) - } + @MainActor func getEntries(for ids: Set) async throws -> [FeedlyEntry] { + + guard !isSuspended else { throw TransportError.suspended } + + var request = try urlRequest(path: "/v3/entries/.mget", method: HTTPMethod.post, includeJSONHeaders: true, includeOAuthToken: true) + let body = Array(ids) + try addObject(body, to: &request) + + let (_, entries) = try await send(request: request, resultType: [FeedlyEntry].self) + + guard let entries else { + throw URLError(.cannotDecodeContentData) } + + return entries } } @@ -604,14 +569,10 @@ extension FeedlyAPICaller: FeedlyMarkArticlesService { extension FeedlyAPICaller: FeedlySearchService { - func getFeeds(for query: String, count: Int, locale: String, completion: @escaping (Result) -> ()) { - - guard !isSuspended else { - return DispatchQueue.main.async { - completion(.failure(TransportError.suspended)) - } - } - + @MainActor func getFeeds(for query: String, count: Int, locale: String) async throws -> FeedlyFeedsSearchResponse { + + guard !isSuspended else { throw TransportError.suspended } + var components = baseURLComponents components.path = "/v3/search/feeds" @@ -620,29 +581,22 @@ extension FeedlyAPICaller: FeedlySearchService { URLQueryItem(name: "count", value: String(count)), URLQueryItem(name: "locale", value: locale) ] - - + guard let url = components.url else { fatalError("\(components) does not produce a valid URL.") } var request = URLRequest(url: url) request.httpMethod = "GET" - request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType) - request.addValue("application/json", forHTTPHeaderField: "Accept-Type") - - send(request: request, resultType: FeedlyFeedsSearchResponse.self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in - switch result { - case .success(let (_, searchResponse)): - if let response = searchResponse { - completion(.success(response)) - } else { - completion(.failure(URLError(.cannotDecodeContentData))) - } - case .failure(let error): - completion(.failure(error)) - } + addJSONHeaders(&request) + + let (_, searchResponse) = try await send(request: request, resultType: FeedlyFeedsSearchResponse.self) + + guard let searchResponse else { + throw URLError(.cannotDecodeContentData) } + + return searchResponse } } diff --git a/Feedly/Sources/Feedly/Operations/FeedlyGetEntriesOperation.swift b/Feedly/Sources/Feedly/Operations/FeedlyGetEntriesOperation.swift index 62548c699..64e5ced70 100644 --- a/Feedly/Sources/Feedly/Operations/FeedlyGetEntriesOperation.swift +++ b/Feedly/Sources/Feedly/Operations/FeedlyGetEntriesOperation.swift @@ -54,13 +54,14 @@ public final class FeedlyGetEntriesOperation: FeedlyOperation, FeedlyEntryProvid } public override func run() { - service.getEntries(for: provider.entryIDs) { result in - switch result { - case .success(let entries): + + Task { @MainActor in + + do { + let entries = try await service.getEntries(for: provider.entryIDs) self.entries = entries self.didFinish() - - case .failure(let error): + } catch { os_log(.debug, log: self.log, "Unable to get entries: %{public}@.", error as NSError) self.didFinish(with: error) } diff --git a/Feedly/Sources/Feedly/Operations/FeedlySearchOperation.swift b/Feedly/Sources/Feedly/Operations/FeedlySearchOperation.swift index e732b9ff1..edf0b0f6f 100644 --- a/Feedly/Sources/Feedly/Operations/FeedlySearchOperation.swift +++ b/Feedly/Sources/Feedly/Operations/FeedlySearchOperation.swift @@ -9,16 +9,18 @@ import Foundation public protocol FeedlySearchService: AnyObject { - func getFeeds(for query: String, count: Int, locale: String, completion: @escaping (Result) -> ()) + + @MainActor func getFeeds(for query: String, count: Int, locale: String) async throws -> FeedlyFeedsSearchResponse } public protocol FeedlySearchOperationDelegate: AnyObject { + @MainActor func feedlySearchOperation(_ operation: FeedlySearchOperation, didGet response: FeedlyFeedsSearchResponse) } /// Find one and only one feed for a given query (usually, a URL). /// What happens when a feed is found for the URL is delegated to the `searchDelegate`. -public class FeedlySearchOperation: FeedlyOperation { +public final class FeedlySearchOperation: FeedlyOperation { let query: String let locale: Locale @@ -32,14 +34,15 @@ public class FeedlySearchOperation: FeedlyOperation { } public override func run() { - searchService.getFeeds(for: query, count: 1, locale: locale.identifier) { result in - switch result { - case .success(let response): - assert(Thread.isMainThread) - self.searchDelegate?.feedlySearchOperation(self, didGet: response) + + Task { @MainActor in + + do { + let searchResponse = try await searchService.getFeeds(for: query, count: 1, locale: locale.identifier) + self.searchDelegate?.feedlySearchOperation(self, didGet: searchResponse) self.didFinish() - case .failure(let error): + } catch { self.didFinish(with: error) } } diff --git a/Feedly/Sources/Feedly/Services/FeedlyGetEntriesService.swift b/Feedly/Sources/Feedly/Services/FeedlyGetEntriesService.swift index 0fefc7f49..728a0f28d 100644 --- a/Feedly/Sources/Feedly/Services/FeedlyGetEntriesService.swift +++ b/Feedly/Sources/Feedly/Services/FeedlyGetEntriesService.swift @@ -9,5 +9,6 @@ import Foundation public protocol FeedlyGetEntriesService: AnyObject { - func getEntries(for ids: Set, completion: @escaping (Result<[FeedlyEntry], Error>) -> ()) + + @MainActor func getEntries(for ids: Set) async throws -> [FeedlyEntry] }