Convert methods to async await.

This commit is contained in:
Brent Simmons 2024-04-23 19:23:13 -07:00
parent f75f67a42c
commit dee503e104
3 changed files with 58 additions and 115 deletions

View File

@ -377,44 +377,24 @@ final class FeedlyAccountDelegate: AccountDelegate {
func renameFeed(for account: Account, with feed: Feed, to name: String) async throws {
try await withCheckedThrowingContinuation { continuation in
self.renameFeed(for: account, with: feed, to: name) { result in
switch result {
case .success:
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
let folderCollectionIDs = account.folders?.filter { $0.has(feed) }.compactMap { $0.externalID }
guard let collectionIDs = folderCollectionIDs, let collectionID = collectionIDs.first else {
completion(.failure(FeedlyAccountDelegateError.unableToRenameFeed(feed.nameForDisplay, name)))
return
throw FeedlyAccountDelegateError.unableToRenameFeed(feed.nameForDisplay, name)
}
let feedID = FeedlyFeedResourceID(id: feed.feedID)
let editedNameBefore = feed.editedName
// Adding an existing feed updates it.
// Updating feed name in one folder/collection updates it for all folders/collections.
caller.addFeed(with: feedID, title: name, toCollectionWith: collectionID) { result in
switch result {
case .success:
completion(.success(()))
case .failure(let error):
feed.editedName = editedNameBefore
completion(.failure(error))
}
}
// optimistically set the name
// Optimistically set the name
feed.editedName = name
do {
// Adding an existing feed updates it.
// Updating feed name in one folder/collection updates it for all folders/collections.
try await caller.addFeed(with: feedID, title: name, toCollectionWith: collectionID)
} catch {
feed.editedName = editedNameBefore
}
}
func addFeed(for account: Account, with feed: Feed, to container: any Container) async throws {

View File

@ -155,7 +155,7 @@ protocol FeedlyAPICallerDelegate: AnyObject {
guard !isSuspended else { throw TransportError.suspended }
var request = try urlRequest(path: "/v3/opml", method: HTTPMethod.post, addJSONHeaders: false, addOauthToken: true)
var request = try urlRequest(path: "/v3/opml", method: HTTPMethod.post, includeJSONHeaders: false, includeOauthToken: true)
request.addValue("text/xml", forHTTPHeaderField: HTTPRequestHeader.contentType)
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.acceptType)
request.httpBody = opmlData
@ -170,7 +170,7 @@ protocol FeedlyAPICallerDelegate: AnyObject {
guard !isSuspended else { throw TransportError.suspended }
var request = try urlRequest(path: "/v3/collections", method: HTTPMethod.post, addJSONHeaders: true, addOauthToken: true)
var request = try urlRequest(path: "/v3/collections", method: HTTPMethod.post, includeJSONHeaders: true, includeOauthToken: true)
struct CreateCollectionBody: Encodable {
var label: String
@ -191,7 +191,7 @@ protocol FeedlyAPICallerDelegate: AnyObject {
guard !isSuspended else { throw TransportError.suspended }
var request = try urlRequest(path: "/v3/collections", method: HTTPMethod.post, addJSONHeaders: true, addOauthToken: true)
var request = try urlRequest(path: "/v3/collections", method: HTTPMethod.post, includeJSONHeaders: true, includeOauthToken: true)
struct RenameCollectionBody: Encodable {
var id: String
@ -220,7 +220,7 @@ protocol FeedlyAPICallerDelegate: AnyObject {
guard let encodedID = encodeForURLPath(id) else {
throw FeedlyAccountDelegateError.unexpectedResourceID(id)
}
let request = try urlRequest(path: "/v3/collections/\(encodedID)", method: HTTPMethod.delete, addJSONHeaders: true, addOauthToken: true)
let request = try urlRequest(path: "/v3/collections/\(encodedID)", method: HTTPMethod.delete, includeJSONHeaders: true, includeOauthToken: true)
let (httpResponse, _) = try await send(request: request, resultType: Optional<FeedlyCollection>.self)
@ -245,7 +245,7 @@ protocol FeedlyAPICallerDelegate: AnyObject {
var request = URLRequest(url: url)
request.httpMethod = HTTPMethod.delete
_addJSONHeaders(&request)
addJSONHeaders(&request)
try addOauthAccessToken(&request)
struct RemovableFeed: Encodable {
@ -267,63 +267,38 @@ protocol FeedlyAPICallerDelegate: AnyObject {
extension FeedlyAPICaller: FeedlyAddFeedToCollectionService {
@MainActor func addFeed(with feedId: FeedlyFeedResourceID, title: String? = nil, toCollectionWith collectionID: String, completion: @escaping (Result<[FeedlyFeed], 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))
}
}
@MainActor func addFeed(with feedID: FeedlyFeedResourceID, title: String? = nil, toCollectionWith collectionID: String) async throws -> [FeedlyFeed] {
guard !isSuspended else { throw TransportError.suspended }
guard let encodedID = encodeForURLPath(collectionID) else {
return DispatchQueue.main.async {
completion(.failure(FeedlyAccountDelegateError.unexpectedResourceID(collectionID)))
}
throw FeedlyAccountDelegateError.unexpectedResourceID(collectionID)
}
var components = baseURLComponents
components.percentEncodedPath = "/v3/collections/\(encodedID)/feeds"
guard let url = components.url else {
fatalError("\(components) does not produce a valid URL.")
}
var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
do {
struct AddFeedBody: Encodable {
var id: String
var title: String?
}
let encoder = JSONEncoder()
let data = try encoder.encode(AddFeedBody(id: feedId.id, title: title))
request.httpBody = data
} catch {
return DispatchQueue.main.async {
completion(.failure(error))
}
request.httpMethod = HTTPMethod.put
addJSONHeaders(&request)
try addOauthAccessToken(&request)
struct AddFeedBody: Encodable {
var id: String
var title: String?
}
send(request: request, resultType: [FeedlyFeed].self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in
switch result {
case .success((_, let collectionFeeds)):
if let feeds = collectionFeeds {
completion(.success(feeds))
} else {
completion(.failure(URLError(.cannotDecodeContentData)))
}
case .failure(let error):
completion(.failure(error))
}
let encoder = JSONEncoder()
let data = try encoder.encode(AddFeedBody(id: feedID.id, title: title))
request.httpBody = data
let (_, collectionFeeds) = try await send(request: request, resultType: [FeedlyFeed].self)
guard let collectionFeeds else {
throw URLError(.cannotDecodeContentData)
}
return collectionFeeds
}
}
@ -834,24 +809,24 @@ extension FeedlyAPICaller: FeedlyLogoutService {
private extension FeedlyAPICaller {
func urlRequest(path: String, method: String, addJSONHeaders: Bool, addOauthToken: Bool) throws -> URLRequest {
func urlRequest(path: String, method: String, includeJSONHeaders: Bool, includeOauthToken: Bool) throws -> URLRequest {
let url = apiURL(path)
var request = URLRequest(url: url)
request.httpMethod = method
if addJSONHeaders {
_addJSONHeaders(&request)
if includeJSONHeaders {
addJSONHeaders(&request)
}
if addOauthToken {
if includeOauthToken {
try addOauthAccessToken(&request)
}
return request
}
func _addJSONHeaders(_ request: inout URLRequest) {
func addJSONHeaders(_ request: inout URLRequest) {
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")

View File

@ -11,7 +11,7 @@ import CommonErrors
import Feedly
protocol FeedlyAddFeedToCollectionService {
func addFeed(with feedId: FeedlyFeedResourceID, title: String?, toCollectionWith collectionID: String, completion: @escaping (Result<[FeedlyFeed], Error>) -> ())
func addFeed(with feedID: FeedlyFeedResourceID, title: String?, toCollectionWith collectionID: String) async throws -> [FeedlyFeed]
}
final class FeedlyAddFeedToCollectionOperation: FeedlyOperation, FeedlyFeedsAndFoldersProviding, FeedlyResourceProviding {
@ -37,36 +37,24 @@ final class FeedlyAddFeedToCollectionOperation: FeedlyOperation, FeedlyFeedsAndF
}
override func run() {
service.addFeed(with: feedResource, title: feedName, toCollectionWith: collectionID) { [weak self] result in
guard let self = self else {
return
Task { @MainActor in
do {
let feedlyFeeds = try await service.addFeed(with: feedResource, title: feedName, toCollectionWith: collectionID)
feedsAndFolders = [(feedlyFeeds, folder)]
let feedsWithCreatedFeedID = feedlyFeeds.filter { $0.id == resource.id }
if feedsWithCreatedFeedID.isEmpty {
didFinish(with: AccountError.createErrorNotFound)
} else {
didFinish()
}
} catch {
didFinish(with: error)
}
if self.isCanceled {
self.didFinish()
return
}
self.didCompleteRequest(result)
}
}
}
private extension FeedlyAddFeedToCollectionOperation {
func didCompleteRequest(_ result: Result<[FeedlyFeed], Error>) {
switch result {
case .success(let feedlyFeeds):
feedsAndFolders = [(feedlyFeeds, folder)]
let feedsWithCreatedFeedID = feedlyFeeds.filter { $0.id == resource.id }
if feedsWithCreatedFeedID.isEmpty {
didFinish(with: AccountError.createErrorNotFound)
} else {
didFinish()
}
case .failure(let error):
didFinish(with: error)
}
}
}