Convert FeedFinder public API to async/await.

This commit is contained in:
Brent Simmons
2023-09-24 10:38:20 -07:00
parent 5e3086667d
commit a2ab1f221b
4 changed files with 80 additions and 72 deletions

View File

@@ -275,25 +275,26 @@ private extension LocalAccountDelegate {
// with an Untitled name if we don't delay it being added to the sidebar.
BatchUpdate.shared.start()
refreshProgress.addToNumberOfTasksAndRemaining(1)
FeedFinder.find(url: url) { result in
switch result {
case .success(let feedSpecifiers):
Task { @MainActor in
do {
let feedSpecifiers = try await FeedFinder.find(url: url)
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers),
let url = URL(string: bestFeedSpecifier.urlString) else {
self.refreshProgress.completeTask()
BatchUpdate.shared.end()
completion(.failure(AccountError.createErrorNotFound))
return
let url = URL(string: bestFeedSpecifier.urlString) else {
self.refreshProgress.completeTask()
BatchUpdate.shared.end()
completion(.failure(AccountError.createErrorNotFound))
return
}
if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
self.refreshProgress.completeTask()
BatchUpdate.shared.end()
completion(.failure(AccountError.createErrorAlreadySubscribed))
return
}
InitialFeedDownloader.download(url) { parsedFeed in
self.refreshProgress.completeTask()
@@ -310,10 +311,9 @@ private extension LocalAccountDelegate {
BatchUpdate.shared.end()
completion(.failure(AccountError.createErrorNotFound))
}
}
case .failure:
} catch {
BatchUpdate.shared.end()
self.refreshProgress.completeTask()
completion(.failure(AccountError.createErrorNotFound))

View File

@@ -389,19 +389,20 @@ public enum ReaderAPIAccountDelegateError: LocalizedError {
}
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<Feed, Error>) -> Void) {
guard let url = URL(string: url) else {
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
return
}
refreshProgress.addToNumberOfTasksAndRemaining(2)
FeedFinder.find(url: url) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let feedSpecifiers):
let feedSpecifiers = feedSpecifiers.filter { !$0.urlString.contains("json") }
Task { @MainActor in
guard let url = URL(string: url) else {
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
return
}
refreshProgress.addToNumberOfTasksAndRemaining(2)
do {
var feedSpecifiers = try await FeedFinder.find(url: url)
self.refreshProgress.completeTask()
feedSpecifiers = feedSpecifiers.filter { !$0.urlString.contains("json") }
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers) else {
self.refreshProgress.clear()
completion(.failure(AccountError.createErrorNotFound))
@@ -409,32 +410,24 @@ public enum ReaderAPIAccountDelegateError: LocalizedError {
}
self.caller.createSubscription(url: bestFeedSpecifier.urlString, name: name) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let subResult):
switch subResult {
case .created(let subscription):
self.createFeed(account: account, subscription: subscription, name: name, container: container, completion: completion)
case .notFound:
DispatchQueue.main.async {
Task { @MainActor in
self.refreshProgress.completeTask()
switch result {
case .success(let subResult):
switch subResult {
case .created(let subscription):
self.createFeed(account: account, subscription: subscription, name: name, container: container, completion: completion)
case .notFound:
completion(.failure(AccountError.createErrorNotFound))
}
}
case .failure(let error):
DispatchQueue.main.async {
case .failure(let error):
let wrappedError = WrappedAccountError(accountID: account.accountID, accountNameForDisplay: account.nameForDisplay, underlyingError: error)
completion(.failure(wrappedError))
}
}
}
case .failure:
self.refreshProgress.clear()
completion(.failure(AccountError.createErrorNotFound))
}
}
}
func renameFeed(for account: Account, feed: Feed, name: String) async throws {

View File

@@ -629,11 +629,12 @@ private extension CloudKitAccountDelegate {
}
refreshProgress.addToNumberOfTasksAndRemaining(5)
FeedFinder.find(url: url) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let feedSpecifiers):
Task { @MainActor in
do {
let feedSpecifiers = try await FeedFinder.find(url: url)
self.refreshProgress.completeTask()
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers), let url = URL(string: bestFeedSpecifier.urlString) else {
self.refreshProgress.completeTasks(3)
if validateFeed {
@@ -644,13 +645,13 @@ private extension CloudKitAccountDelegate {
}
return
}
if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
self.refreshProgress.completeTasks(4)
completion(.failure(AccountError.createErrorAlreadySubscribed))
return
}
let feed = account.createFeed(with: nil, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil)
feed.editedName = editedName
container.addFeed(feed)
@@ -662,12 +663,12 @@ private extension CloudKitAccountDelegate {
account.update(feed, with: parsedFeed) { result in
switch result {
case .success:
self.accountZone.createFeed(url: bestFeedSpecifier.urlString,
name: parsedFeed.title,
editedName: editedName,
homePageURL: parsedFeed.homePageURL,
container: container) { result in
name: parsedFeed.title,
editedName: editedName,
homePageURL: parsedFeed.homePageURL,
container: container) { result in
self.refreshProgress.completeTask()
switch result {
@@ -680,7 +681,7 @@ private extension CloudKitAccountDelegate {
self.refreshProgress.completeTasks(2)
completion(.failure(error))
}
}
case .failure(let error):
@@ -688,18 +689,17 @@ private extension CloudKitAccountDelegate {
self.refreshProgress.completeTasks(3)
completion(.failure(error))
}
}
} else {
self.refreshProgress.completeTasks(3)
container.removeFeed(feed)
completion(.failure(AccountError.createErrorNotFound))
}
}
case .failure:
self.refreshProgress.completeTasks(3)
} catch {
self.refreshProgress.completeTasks(4)
if validateFeed {
self.refreshProgress.completeTask()
completion(.failure(AccountError.createErrorNotFound))

View File

@@ -14,9 +14,27 @@ import AccountError
public final class FeedFinder {
public static func find(url: URL, completion: @escaping (Result<Set<FeedSpecifier>, Error>) -> Void) {
@MainActor public static func find(url: URL) async throws -> Set<FeedSpecifier> {
try await withCheckedThrowingContinuation { continuation in
find(url: url) { result in
switch result {
case .success(let feedSpecifiers):
continuation.resume(returning: feedSpecifiers)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}
private extension FeedFinder {
@MainActor static func find(url: URL, completion: @escaping (Result<Set<FeedSpecifier>, Error>) -> Void) {
downloadAddingToCache(url) { (data, response, error) in
if response?.forcedStatusCode == 404 {
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), urlComponents.host == "micro.blog" {
urlComponents.path = "\(urlComponents.path).json"
@@ -29,39 +47,36 @@ public final class FeedFinder {
}
return
}
if let error = error {
completion(.failure(error))
return
}
guard let data = data, let response = response else {
completion(.failure(AccountError.createErrorNotFound))
return
}
if !response.statusIsOK || data.isEmpty {
completion(.failure(AccountError.createErrorNotFound))
return
}
if FeedFinder.isFeed(data, url.absoluteString) {
let feedSpecifier = FeedSpecifier(title: nil, urlString: url.absoluteString, source: .UserEntered, orderFound: 1)
completion(.success(Set([feedSpecifier])))
return
}
if !FeedFinder.isHTML(data) {
completion(.failure(AccountError.createErrorNotFound))
return
}
FeedFinder.findFeedsInHTMLPage(htmlData: data, urlString: url.absoluteString, completion: completion)
}
}
}
private extension FeedFinder {
static func addFeedSpecifier(_ feedSpecifier: FeedSpecifier, feedSpecifiers: inout [String: FeedSpecifier]) {
// If theres an existing feed specifier, merge the two so that we have the best data. If one has a title and one doesnt, use that non-nil title. Use the better source.