From 9f67b3f4985bb1f1ce5c1d38d6fd4a56d2441e8a Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Thu, 4 Apr 2024 22:05:46 -0700 Subject: [PATCH] Convert account.update to async await. --- Account/Sources/Account/Account.swift | 46 +++++-------------- .../CloudKit/CloudKitAccountDelegate.swift | 24 ++++++---- .../Feedbin/FeedbinAccountDelegate.swift | 37 +++++++++------ .../Feedly/FeedlyAccountDelegate.swift | 24 +++++----- ...UpdateAccountFeedsWithItemsOperation.swift | 14 +++--- .../LocalAccount/LocalAccountDelegate.swift | 11 +---- .../NewsBlur/NewsBlurAccountDelegate.swift | 37 ++++++++------- .../ReaderAPI/ReaderAPIAccountDelegate.swift | 29 ++++++------ .../ArticlesDatabaseCompatibility.swift | 22 ++++----- 9 files changed, 119 insertions(+), 125 deletions(-) diff --git a/Account/Sources/Account/Account.swift b/Account/Sources/Account/Account.swift index 30b42b49d..15ba6dbbb 100644 --- a/Account/Sources/Account/Account.swift +++ b/Account/Sources/Account/Account.swift @@ -809,56 +809,34 @@ public enum FetchType { return articleChanges } - func update(feedIDsAndItems: [String: Set], defaultRead: Bool, completion: @escaping DatabaseCompletionBlock) { + func update(feedIDsAndItems: [String: Set], defaultRead: Bool) async throws { precondition(Thread.isMainThread) precondition(type != .onMyMac && type != .cloudKit) guard !feedIDsAndItems.isEmpty else { - completion(nil) return } - database.update(feedIDsAndItems: feedIDsAndItems, defaultRead: defaultRead) { updateArticlesResult in - - MainActor.assumeIsolated { - switch updateArticlesResult { - case .success(let newAndUpdatedArticles): - self.sendNotificationAbout(newAndUpdatedArticles) - completion(nil) - case .failure(let databaseError): - completion(databaseError) - } - } - } + let articleChanges = try await database.update(feedIDsAndItems: feedIDsAndItems, defaultRead: defaultRead) + sendNotificationAbout(articleChanges) } - func update(_ articles: Set
, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping ArticleSetResultBlock) { + func update(articles: Set
, statusKey: ArticleStatus.Key, flag: Bool) async throws -> Set
{ - // Returns set of Articles whose statuses did change. + // Return set of Articles whose statuses did change. - guard !articles.isEmpty else { - completion(.success(Set
())) - return + if articles.isEmpty { + return Set
() } - Task { @MainActor in + let updatedStatuses = try await database.mark(articles: articles, statusKey: statusKey, flag: flag) ?? Set() - do { - var updatedArticles = Set
() + let updatedArticleIDs = updatedStatuses.articleIDs() + let updatedArticles = Set(articles.filter{ updatedArticleIDs.contains($0.articleID) }) + self.noteStatusesForArticlesDidChange(updatedArticles) - if let updatedStatuses = try await database.mark(articles: articles, statusKey: statusKey, flag: flag) { - let updatedArticleIDs = updatedStatuses.articleIDs() - updatedArticles = Set(articles.filter{ updatedArticleIDs.contains($0.articleID) }) - self.noteStatusesForArticlesDidChange(updatedArticles) - } - - completion(.success(updatedArticles)) - - } catch { - completion(.failure(.suspended)) - } - } + return updatedArticles } /// Make sure statuses exist. Any existing statuses won’t be touched. diff --git a/Account/Sources/Account/CloudKit/CloudKitAccountDelegate.swift b/Account/Sources/Account/CloudKit/CloudKitAccountDelegate.swift index ae3bc08e7..67a6dcd36 100644 --- a/Account/Sources/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Account/Sources/Account/CloudKit/CloudKitAccountDelegate.swift @@ -616,21 +616,25 @@ enum CloudKitAccountDelegateError: LocalizedError { } private func markArticles(for account: Account, articles: Set
, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping (Result) -> Void) { - account.update(articles, statusKey: statusKey, flag: flag) { result in - switch result { - case .success(let articles): + + Task { @MainActor in + + do { + + let articles = try await account.update(articles: articles, statusKey: statusKey, flag: flag) + let syncStatuses = articles.map { article in return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag) } - Task { @MainActor in - try? await self.database.insertStatuses(syncStatuses) - if let count = try? await self.database.selectPendingCount(), count > 100 { - self.sendArticleStatus(for: account, showProgress: false) { _ in } - } - completion(.success(())) + try? await self.database.insertStatuses(syncStatuses) + if let count = try? await self.database.selectPendingCount(), count > 100 { + self.sendArticleStatus(for: account, showProgress: false) { _ in } } - case .failure(let error): + + completion(.success(())) + + } catch { completion(.failure(error)) } } diff --git a/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift b/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift index 66b93fa07..e284205d8 100644 --- a/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift +++ b/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift @@ -751,23 +751,25 @@ final class FeedbinAccountDelegate: AccountDelegate { } private func markArticles(for account: Account, articles: Set
, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping (Result) -> Void) { - account.update(articles, statusKey: statusKey, flag: flag) { result in - switch result { - case .success(let articles): + + Task { @MainActor in + + do { + + let articles = try await account.update(articles: articles, statusKey: statusKey, flag: flag) + let syncStatuses = articles.map { article in return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag) } - Task { @MainActor in - try? await self.database.insertStatuses(syncStatuses) + try? await self.database.insertStatuses(syncStatuses) - if let count = try? await self.database.selectPendingCount(), count > 100 { - self.sendArticleStatus(for: account) { _ in } - } - completion(.success(())) + if let count = try? await self.database.selectPendingCount(), count > 100 { + self.sendArticleStatus(for: account) { _ in } } + completion(.success(())) - case .failure(let error): + } catch { completion(.failure(error)) } } @@ -1467,13 +1469,22 @@ private extension FeedbinAccountDelegate { } } } - + func processEntries(account: Account, entries: [FeedbinEntry]?, completion: @escaping DatabaseCompletionBlock) { + let parsedItems = mapEntriesToParsedItems(entries: entries) let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) } - account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true, completion: completion) + + Task { @MainActor in + do { + try await account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) + completion(nil) + } catch { + completion(.suspended) + } + } } - + func mapEntriesToParsedItems(entries: [FeedbinEntry]?) -> Set { guard let entries = entries else { return Set() diff --git a/Account/Sources/Account/Feedly/FeedlyAccountDelegate.swift b/Account/Sources/Account/Feedly/FeedlyAccountDelegate.swift index 1bc8a1d30..75434b32c 100644 --- a/Account/Sources/Account/Feedly/FeedlyAccountDelegate.swift +++ b/Account/Sources/Account/Feedly/FeedlyAccountDelegate.swift @@ -730,23 +730,25 @@ final class FeedlyAccountDelegate: AccountDelegate { } private func markArticles(for account: Account, articles: Set
, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping (Result) -> Void) { - account.update(articles, statusKey: statusKey, flag: flag) { result in - switch result { - case .success(let articles): + + Task { @MainActor in + + do { + + let articles = try await account.update(articles: articles, statusKey: statusKey, flag: flag) + let syncStatuses = articles.map { article in return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag) } - Task { @MainActor in + try? await self.database.insertStatuses(syncStatuses) - try? await self.database.insertStatuses(syncStatuses) - - if let count = try? await self.database.selectPendingCount(), count > 100 { - self.sendArticleStatus(for: account) { _ in } - } - completion(.success(())) + if let count = try? await self.database.selectPendingCount(), count > 100 { + self.sendArticleStatus(for: account) { _ in } } - case .failure(let error): + completion(.success(())) + + } catch { completion(.failure(error)) } } diff --git a/Account/Sources/Account/Feedly/Operations/FeedlyUpdateAccountFeedsWithItemsOperation.swift b/Account/Sources/Account/Feedly/Operations/FeedlyUpdateAccountFeedsWithItemsOperation.swift index 093ccd9e1..462d9a676 100644 --- a/Account/Sources/Account/Feedly/Operations/FeedlyUpdateAccountFeedsWithItemsOperation.swift +++ b/Account/Sources/Account/Feedly/Operations/FeedlyUpdateAccountFeedsWithItemsOperation.swift @@ -19,23 +19,25 @@ final class FeedlyUpdateAccountFeedsWithItemsOperation: FeedlyOperation { private let log: OSLog init(account: Account, organisedItemsProvider: FeedlyParsedItemsByFeedProviding, log: OSLog) { + self.account = account self.organisedItemsProvider = organisedItemsProvider self.log = log } override func run() { + let feedIDsAndItems = organisedItemsProvider.parsedItemsKeyedByFeedId - account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) { databaseError in - MainActor.assumeIsolated { - if let error = databaseError { - self.didFinish(with: error) - return - } + Task { @MainActor in + do { + try await account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) os_log(.debug, log: self.log, "Updated %i feeds for \"%@\"", feedIDsAndItems.count, self.organisedItemsProvider.parsedItemsByFeedProviderName) self.didFinish() + + } catch { + self.didFinish(with: error) } } } diff --git a/Account/Sources/Account/LocalAccount/LocalAccountDelegate.swift b/Account/Sources/Account/LocalAccount/LocalAccountDelegate.swift index 3b5ee2418..7619ef3fc 100644 --- a/Account/Sources/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Account/Sources/Account/LocalAccount/LocalAccountDelegate.swift @@ -142,16 +142,7 @@ final class LocalAccountDelegate: AccountDelegate { func markArticles(for account: Account, articles: Set
, statusKey: ArticleStatus.Key, flag: Bool) async throws { - try await withCheckedThrowingContinuation { continuation in - account.update(articles, statusKey: statusKey, flag: flag) { result in - switch result { - case .success: - continuation.resume() - case .failure(let error): - continuation.resume(throwing: error) - } - } - } + try await account.update(articles: articles, statusKey: statusKey, flag: flag) } func accountDidInitialize(_ account: Account) { diff --git a/Account/Sources/Account/NewsBlur/NewsBlurAccountDelegate.swift b/Account/Sources/Account/NewsBlur/NewsBlurAccountDelegate.swift index 047cb2a8b..413a8cced 100644 --- a/Account/Sources/Account/NewsBlur/NewsBlurAccountDelegate.swift +++ b/Account/Sources/Account/NewsBlur/NewsBlurAccountDelegate.swift @@ -368,6 +368,7 @@ final class NewsBlurAccountDelegate: AccountDelegate { } func processStories(account: Account, stories: [NewsBlurStory]?, since: Date? = nil, completion: @escaping (Result) -> Void) { + let parsedItems = mapStoriesToParsedItems(stories: stories).filter { guard let datePublished = $0.datePublished, let since = since else { return true @@ -379,13 +380,13 @@ final class NewsBlurAccountDelegate: AccountDelegate { Set($0) } - account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) { error in - if let error = error { - completion(.failure(error)) - return + Task { @MainActor in + do { + try await account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) + completion(.success(!feedIDsAndItems.isEmpty)) + } catch { + completion(.failure(.suspended)) } - - completion(.success(!feedIDsAndItems.isEmpty)) } } @@ -788,23 +789,25 @@ final class NewsBlurAccountDelegate: AccountDelegate { } private func markArticles(for account: Account, articles: Set
, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping (Result) -> Void) { - account.update(articles, statusKey: statusKey, flag: flag) { result in - switch result { - case .success(let articles): + + Task { @MainActor in + + do { + + let articles = try await account.update(articles: articles, statusKey: statusKey, flag: flag) + let syncStatuses = articles.map { article in return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag) } - Task { @MainActor in + try? await self.database.insertStatuses(syncStatuses) - try? await self.database.insertStatuses(syncStatuses) - - if let count = try? await self.database.selectPendingCount(), count > 100 { - self.sendArticleStatus(for: account) { _ in } - } - completion(.success(())) + if let count = try? await self.database.selectPendingCount(), count > 100 { + self.sendArticleStatus(for: account) { _ in } } - case .failure(let error): + completion(.success(())) + + } catch { completion(.failure(error)) } } diff --git a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift index 899d84841..40428255b 100644 --- a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift +++ b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift @@ -803,25 +803,25 @@ final class ReaderAPIAccountDelegate: AccountDelegate { } private func markArticles(for account: Account, articles: Set
, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping (Result) -> Void) { - account.update(articles, statusKey: statusKey, flag: flag) { result in - switch result { - case .success(let articles): + + Task { @MainActor in + + do { + let articles = try await account.update(articles: articles, statusKey: statusKey, flag: flag) + let syncStatuses = articles.map { article in return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag) } - Task { @MainActor in + try? await self.database.insertStatuses(syncStatuses) - try? await self.database.insertStatuses(syncStatuses) - - if let count = try? await self.database.selectPendingCount(), count > 100 { - self.sendArticleStatus(for: account) { _ in } - } - - completion(.success(())) + if let count = try? await self.database.selectPendingCount(), count > 100 { + self.sendArticleStatus(for: account) { _ in } } - case .failure(let error): + completion(.success(())) + + } catch { completion(.failure(error)) } } @@ -1246,9 +1246,12 @@ private extension ReaderAPIAccountDelegate { } func processEntries(account: Account, entries: [ReaderAPIEntry]?, completion: @escaping VoidCompletionBlock) { + let parsedItems = mapEntriesToParsedItems(account: account, entries: entries) let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) } - account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) { _ in + + Task { @MainActor in + try? await account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) completion() } } diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabaseCompatibility.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabaseCompatibility.swift index 5e90831d2..32f611686 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabaseCompatibility.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabaseCompatibility.swift @@ -38,17 +38,17 @@ public extension ArticlesDatabase { // } /// Update articles and save new ones — for sync systems (Feedbin, Feedly, etc.). - nonisolated func update(feedIDsAndItems: [String: Set], defaultRead: Bool, completion: @escaping UpdateArticlesCompletionBlock) { - - Task { - do { - let articleChanges = try await update(feedIDsAndItems: feedIDsAndItems, defaultRead: defaultRead) - callUpdateArticlesCompletion(completion, .success(articleChanges)) - } catch { - callUpdateArticlesCompletion(completion, .failure(.suspended)) - } - } - } +// nonisolated func update(feedIDsAndItems: [String: Set], defaultRead: Bool, completion: @escaping UpdateArticlesCompletionBlock) { +// +// Task { +// do { +// let articleChanges = try await update(feedIDsAndItems: feedIDsAndItems, defaultRead: defaultRead) +// callUpdateArticlesCompletion(completion, .success(articleChanges)) +// } catch { +// callUpdateArticlesCompletion(completion, .failure(.suspended)) +// } +// } +// } nonisolated private func callUpdateArticlesCompletion(_ completion: @escaping UpdateArticlesCompletionBlock, _ result: UpdateArticlesResult) {