Convert various status-marking methods to async await.

This commit is contained in:
Brent Simmons 2024-04-05 18:03:03 -07:00
parent cc276dad2a
commit 851296144e
7 changed files with 167 additions and 251 deletions

View File

@ -822,6 +822,7 @@ public enum FetchType {
sendNotificationAbout(articleChanges)
}
@discardableResult
func update(articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool) async throws -> Set<Article> {
// Return set of Articles whose statuses did change.
@ -854,46 +855,38 @@ public enum FetchType {
/// Mark articleIDs statuses based on statusKey and flag.
/// Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification.
/// Returns a set of new article statuses.
func mark(articleIDs: Set<String>, statusKey: ArticleStatus.Key, flag: Bool, completion: DatabaseCompletionBlock? = nil) {
guard !articleIDs.isEmpty else {
completion?(nil)
func mark(articleIDs: Set<String>, statusKey: ArticleStatus.Key, flag: Bool) async throws {
if articleIDs.isEmpty {
return
}
Task { @MainActor in
do {
try await database.mark(articleIDs: articleIDs, statusKey: statusKey, flag: flag)
self.noteStatusesForArticleIDsDidChange(articleIDs: articleIDs, statusKey: statusKey, flag: flag)
completion?(nil)
} catch {
completion?(.suspended)
}
}
try await database.mark(articleIDs: articleIDs, statusKey: statusKey, flag: flag)
self.noteStatusesForArticleIDsDidChange(articleIDs: articleIDs, statusKey: statusKey, flag: flag)
}
/// Mark articleIDs as read. Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification.
/// Returns a set of new article statuses.
func markAsRead(_ articleIDs: Set<String>, completion: DatabaseCompletionBlock? = nil) {
mark(articleIDs: articleIDs, statusKey: .read, flag: true, completion: completion)
func markAsRead(_ articleIDs: Set<String>) async throws {
try await mark(articleIDs: articleIDs, statusKey: .read, flag: true)
}
/// Mark articleIDs as unread. Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification.
/// Returns a set of new article statuses.
func markAsUnread(_ articleIDs: Set<String>, completion: DatabaseCompletionBlock? = nil) {
mark(articleIDs: articleIDs, statusKey: .read, flag: false, completion: completion)
func markAsUnread(_ articleIDs: Set<String>) async throws {
try await mark(articleIDs: articleIDs, statusKey: .read, flag: false)
}
/// Mark articleIDs as starred. Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification.
/// Returns a set of new article statuses.
func markAsStarred(_ articleIDs: Set<String>, completion: DatabaseCompletionBlock? = nil) {
mark(articleIDs: articleIDs, statusKey: .starred, flag: true, completion: completion)
func markAsStarred(_ articleIDs: Set<String>) async throws {
try await mark(articleIDs: articleIDs, statusKey: .starred, flag: true)
}
/// Mark articleIDs as unstarred. Will create statuses in the database and in memory as needed. Sends a .StatusesDidChange notification.
/// Returns a set of new article statuses.
func markAsUnstarred(_ articleIDs: Set<String>, completion: DatabaseCompletionBlock? = nil) {
mark(articleIDs: articleIDs, statusKey: .starred, flag: false, completion: completion)
func markAsUnstarred(_ articleIDs: Set<String>) async throws {
try await mark(articleIDs: articleIDs, statusKey: .starred, flag: false)
}
// Delete the articles associated with the given set of articleIDs

View File

@ -86,90 +86,79 @@ private extension CloudKitArticlesZoneDelegate {
let updateableReadArticleIDs = receivedReadArticleIDs.subtracting(pendingReadStatusArticleIDs)
let updateableUnstarredArticleIDs = receivedUnstarredArticleIDs.subtracting(pendingStarredStatusArticleIDs)
let updateableStarredArticleIDs = receivedStarredArticleIDs.subtracting(pendingStarredStatusArticleIDs)
Task { @MainActor in
var errorOccurred = false
let group = DispatchGroup()
var errorOccurred = false
group.enter()
account?.markAsUnread(updateableUnreadArticleIDs) { databaseError in
MainActor.assumeIsolated {
if let databaseError {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing unread statuses: %@", databaseError.localizedDescription)
}
group.leave()
do {
try await account?.markAsUnread(updateableUnreadArticleIDs)
} catch {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing unread statuses: %@", error.localizedDescription)
}
}
group.enter()
account?.markAsRead(updateableReadArticleIDs) { databaseError in
MainActor.assumeIsolated {
if let databaseError {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing read statuses: %@", databaseError.localizedDescription)
}
group.leave()
do {
try await account?.markAsRead(updateableReadArticleIDs)
} catch {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing read statuses: %@", error.localizedDescription)
}
}
group.enter()
account?.markAsUnstarred(updateableUnstarredArticleIDs) { databaseError in
MainActor.assumeIsolated {
if let databaseError {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing unstarred statuses: %@", databaseError.localizedDescription)
}
group.leave()
do {
try await account?.markAsUnstarred(updateableUnstarredArticleIDs)
} catch {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing unstarred statuses: %@", error.localizedDescription)
}
}
group.enter()
account?.markAsStarred(updateableStarredArticleIDs) { databaseError in
MainActor.assumeIsolated {
if let databaseError {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing starred statuses: %@", databaseError.localizedDescription)
}
group.leave()
do {
try await account?.markAsStarred(updateableStarredArticleIDs)
} catch {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing starred statuses: %@", error.localizedDescription)
}
}
group.enter()
compressionQueue.async {
let parsedItems = records.compactMap { self.makeParsedItem($0) }
let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
let group = DispatchGroup()
group.enter()
Task { @MainActor in
for (feedID, parsedItems) in feedIDsAndItems {
group.enter()
compressionQueue.async {
let parsedItems = records.compactMap { Self.makeParsedItem($0) }
let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
do {
Task { @MainActor in
for (feedID, parsedItems) in feedIDsAndItems {
group.enter()
let articleChanges = try await self.account?.update(feedID: feedID, with: parsedItems, deleteOlder: false)
do {
guard let deletes = articleChanges?.deletedArticles, !deletes.isEmpty else {
let articleChanges = try await self.account?.update(feedID: feedID, with: parsedItems, deleteOlder: false)
guard let deletes = articleChanges?.deletedArticles, !deletes.isEmpty else {
group.leave()
return
}
let syncStatuses = deletes.map { SyncStatus(articleID: $0.articleID, key: .deleted, flag: true) }
try? await self.database.insertStatuses(syncStatuses)
group.leave()
} catch {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing articles: %@", error.localizedDescription)
group.leave()
return
}
let syncStatuses = deletes.map { SyncStatus(articleID: $0.articleID, key: .deleted, flag: true) }
try? await self.database.insertStatuses(syncStatuses)
group.leave()
} catch {
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing articles: %@", error.localizedDescription)
group.leave()
}
group.leave()
}
}
}
group.notify(queue: DispatchQueue.main) {
if errorOccurred {
completion(.failure(CloudKitZoneError.unknown))
} else {
completion(.success(()))
group.notify(queue: DispatchQueue.main) {
if errorOccurred {
completion(.failure(CloudKitZoneError.unknown))
} else {
completion(.success(()))
}
}
}
}
@ -178,7 +167,7 @@ private extension CloudKitArticlesZoneDelegate {
return String(externalID[externalID.index(externalID.startIndex, offsetBy: 2)..<externalID.endIndex])
}
func makeParsedItem(_ articleRecord: CKRecord) -> ParsedItem? {
static func makeParsedItem(_ articleRecord: CKRecord) -> ParsedItem? {
guard articleRecord.recordType == CloudKitArticlesZone.CloudKitArticle.recordType else {
return nil
}

View File

@ -1518,25 +1518,15 @@ private extension FeedbinAccountDelegate {
return
}
let group = DispatchGroup()
// Mark articles as unread
let deltaUnreadArticleIDs = updatableFeedbinUnreadArticleIDs.subtracting(currentUnreadArticleIDs)
group.enter()
account.markAsUnread(deltaUnreadArticleIDs) { _ in
group.leave()
}
try? await account.markAsUnread(deltaUnreadArticleIDs)
// Mark articles as read
let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(updatableFeedbinUnreadArticleIDs)
group.enter()
account.markAsRead(deltaReadArticleIDs) { _ in
group.leave()
}
try? await account.markAsRead(deltaReadArticleIDs)
group.notify(queue: DispatchQueue.main) {
completion()
}
completion()
} catch {
os_log(.error, log: self.log, "Sync Article Read Status failed: %@.", error.localizedDescription)
@ -1545,7 +1535,8 @@ private extension FeedbinAccountDelegate {
}
func syncArticleStarredState(account: Account, articleIDs: [Int]?, completion: @escaping (() -> Void)) {
guard let articleIDs = articleIDs else {
guard let articleIDs else {
completion()
return
}
@ -1563,25 +1554,15 @@ private extension FeedbinAccountDelegate {
return
}
let group = DispatchGroup()
// Mark articles as starred
let deltaStarredArticleIDs = updatableFeedbinStarredArticleIDs.subtracting(currentStarredArticleIDs)
group.enter()
account.markAsStarred(deltaStarredArticleIDs) { _ in
group.leave()
}
try? await account.markAsStarred(deltaStarredArticleIDs)
// Mark articles as unstarred
let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(updatableFeedbinStarredArticleIDs)
group.enter()
account.markAsUnstarred(deltaUnstarredArticleIDs) { _ in
group.leave()
}
try? await account.markAsUnstarred(deltaUnstarredArticleIDs)
group.notify(queue: DispatchQueue.main) {
completion()
}
completion()
} catch {
os_log(.error, log: self.log, "Sync Article Starred Status failed: %@.", error.localizedDescription)

View File

@ -109,46 +109,36 @@ final class FeedlyIngestStarredArticleIdsOperation: FeedlyOperation {
}
func processStarredArticleIDs(_ localStarredArticleIDs: Set<String>) {
guard !isCanceled else {
didFinish()
return
}
let remoteStarredArticleIDs = remoteEntryIds
let group = DispatchGroup()
final class StarredStatusResults {
Task { @MainActor in
var markAsStarredError: Error?
var markAsUnstarredError: Error?
}
let results = StarredStatusResults()
group.enter()
account.markAsStarred(remoteStarredArticleIDs) { error in
if let error {
results.markAsStarredError = error
}
group.leave()
}
let deltaUnstarredArticleIDs = localStarredArticleIDs.subtracting(remoteStarredArticleIDs)
group.enter()
account.markAsUnstarred(deltaUnstarredArticleIDs) { error in
if let error {
results.markAsUnstarredError = error
let remoteStarredArticleIDs = remoteEntryIds
do {
try await account.markAsStarred(remoteStarredArticleIDs)
} catch {
markAsStarredError = error
}
let deltaUnstarredArticleIDs = localStarredArticleIDs.subtracting(remoteStarredArticleIDs)
do {
try await account.markAsUnstarred(deltaUnstarredArticleIDs)
} catch {
markAsUnstarredError = error
}
group.leave()
}
group.notify(queue: .main) {
let markingError = results.markAsStarredError ?? results.markAsUnstarredError
guard let error = markingError else {
self.didFinish()
return
if let markingError = markAsStarredError ?? markAsUnstarredError {
self.didFinish(with: markingError)
}
self.didFinish(with: error)
self.didFinish()
}
}
}

View File

@ -110,45 +110,37 @@ final class FeedlyIngestUnreadArticleIdsOperation: FeedlyOperation {
}
private func processUnreadArticleIDs(_ localUnreadArticleIDs: Set<String>) {
guard !isCanceled else {
didFinish()
return
}
let remoteUnreadArticleIDs = remoteEntryIds
let group = DispatchGroup()
final class ReadStatusResults {
Task { @MainActor in
var markAsUnreadError: Error?
var markAsReadError: Error?
}
let results = ReadStatusResults()
group.enter()
account.markAsUnread(remoteUnreadArticleIDs) { error in
if let error {
results.markAsUnreadError = error
}
group.leave()
}
let articleIDsToMarkRead = localUnreadArticleIDs.subtracting(remoteUnreadArticleIDs)
group.enter()
account.markAsRead(articleIDsToMarkRead) { error in
if let error {
results.markAsReadError = error
do {
try await account.markAsUnread(remoteUnreadArticleIDs)
} catch {
markAsUnreadError = error
}
group.leave()
}
group.notify(queue: .main) {
let markingError = results.markAsReadError ?? results.markAsUnreadError
guard let error = markingError else {
self.didFinish()
return
let articleIDsToMarkRead = localUnreadArticleIDs.subtracting(remoteUnreadArticleIDs)
do {
try await account.markAsRead(articleIDsToMarkRead)
} catch {
markAsReadError = error
}
self.didFinish(with: error)
if let markingError = markAsReadError ?? markAsUnreadError {
self.didFinish(with: markingError)
}
self.didFinish()
}
}
}

View File

@ -342,25 +342,16 @@ extension NewsBlurAccountDelegate {
return
}
let group = DispatchGroup()
// Mark articles as unread
let deltaUnreadArticleIDs = updatableNewsBlurUnreadStoryHashes.subtracting(currentUnreadArticleIDs)
group.enter()
account.markAsUnread(deltaUnreadArticleIDs) { _ in
group.leave()
}
try? await account.markAsUnread(deltaUnreadArticleIDs)
// Mark articles as read
let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(updatableNewsBlurUnreadStoryHashes)
group.enter()
account.markAsRead(deltaReadArticleIDs) { _ in
group.leave()
}
try? await account.markAsRead(deltaReadArticleIDs)
completion()
group.notify(queue: DispatchQueue.main) {
completion()
}
} catch {
os_log(.error, log: self.log, "Sync Story Read Status failed: %@.", error.localizedDescription)
}
@ -376,7 +367,7 @@ extension NewsBlurAccountDelegate {
Task { @MainActor in
do {
let pendingArticleIDs = (try await database.selectPendingStarredStatusArticleIDs()) ?? Set<String>()
let pendingArticleIDs = (try await self.database.selectPendingStarredStatusArticleIDs()) ?? Set<String>()
let newsBlurStarredStoryHashes = Set(hashes.map { $0.hash } )
let updatableNewsBlurUnreadStoryHashes = newsBlurStarredStoryHashes.subtracting(pendingArticleIDs)
@ -386,25 +377,16 @@ extension NewsBlurAccountDelegate {
return
}
let group = DispatchGroup()
// Mark articles as starred
let deltaStarredArticleIDs = updatableNewsBlurUnreadStoryHashes.subtracting(currentStarredArticleIDs)
group.enter()
account.markAsStarred(deltaStarredArticleIDs) { _ in
group.leave()
}
try? await account.markAsStarred(deltaStarredArticleIDs)
// Mark articles as unstarred
let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(updatableNewsBlurUnreadStoryHashes)
group.enter()
account.markAsUnstarred(deltaUnstarredArticleIDs) { _ in
group.leave()
}
try? await account.markAsUnstarred(deltaUnstarredArticleIDs)
completion()
group.notify(queue: DispatchQueue.main) {
completion()
}
} catch {
os_log(.error, log: self.log, "Sync Story Starred Status failed: %@.", error.localizedDescription)
}

View File

@ -128,21 +128,24 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
self.refreshProgress.completeTask()
self.caller.retrieveItemIDs(type: .allForAccount) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let articleIDs):
account.markAsRead(Set(articleIDs)) { _ in
MainActor.assumeIsolated {
self.refreshArticleStatus(for: account) { _ in
self.refreshProgress.completeTask()
self.refreshMissingArticles(account) {
self.refreshProgress.clear()
DispatchQueue.main.async {
completion(.success(()))
}
Task { @MainActor in
try? await account.markAsRead(Set(articleIDs))
self.refreshArticleStatus(for: account) { _ in
self.refreshProgress.completeTask()
self.refreshMissingArticles(account) {
self.refreshProgress.clear()
DispatchQueue.main.async {
completion(.success(()))
}
}
}
}
case .failure(let error):
completion(.failure(error))
}
@ -1170,26 +1173,28 @@ private extension ReaderAPIAccountDelegate {
self.caller.retrieveItemIDs(type: .allForFeed, feedID: feed.feedID) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let articleIDs):
account.markAsRead(Set(articleIDs)) { _ in
MainActor.assumeIsolated {
Task { @MainActor in
try? await account.markAsRead(Set(articleIDs))
self.refreshProgress.completeTask()
self.refreshArticleStatus(for: account) { _ in
self.refreshProgress.completeTask()
self.refreshArticleStatus(for: account) { _ in
self.refreshProgress.completeTask()
self.refreshMissingArticles(account) {
self.refreshProgress.clear()
DispatchQueue.main.async {
completion(.success(feed))
}
self.refreshMissingArticles(account) {
self.refreshProgress.clear()
DispatchQueue.main.async {
completion(.success(feed))
}
}
}
}
case .failure(let error):
completion(.failure(error))
}
}
}
@ -1298,7 +1303,8 @@ private extension ReaderAPIAccountDelegate {
}
func syncArticleReadState(account: Account, articleIDs: [String]?, completion: @escaping (() -> Void)) {
guard let articleIDs = articleIDs else {
guard let articleIDs else {
completion()
return
}
@ -1314,26 +1320,17 @@ private extension ReaderAPIAccountDelegate {
completion()
return
}
let group = DispatchGroup()
// Mark articles as unread
let deltaUnreadArticleIDs = updatableReaderUnreadArticleIDs.subtracting(currentUnreadArticleIDs)
group.enter()
account.markAsUnread(deltaUnreadArticleIDs) { _ in
group.leave()
}
try? await account.markAsUnread(deltaUnreadArticleIDs)
// Mark articles as read
let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(updatableReaderUnreadArticleIDs)
group.enter()
account.markAsRead(deltaReadArticleIDs) { _ in
group.leave()
}
try? await account.markAsRead(deltaReadArticleIDs)
group.notify(queue: DispatchQueue.main) {
completion()
}
completion()
} catch {
os_log(.error, log: self.log, "Sync Article Read Status failed: %@.", error.localizedDescription)
}
@ -1341,7 +1338,8 @@ private extension ReaderAPIAccountDelegate {
}
func syncArticleStarredState(account: Account, articleIDs: [String]?, completion: @escaping (() -> Void)) {
guard let articleIDs = articleIDs else {
guard let articleIDs else {
completion()
return
}
@ -1359,25 +1357,16 @@ private extension ReaderAPIAccountDelegate {
return
}
let group = DispatchGroup()
// Mark articles as starred
let deltaStarredArticleIDs = updatableReaderUnreadArticleIDs.subtracting(currentStarredArticleIDs)
group.enter()
account.markAsStarred(deltaStarredArticleIDs) { _ in
group.leave()
}
try? await account.markAsStarred(deltaStarredArticleIDs)
// Mark articles as unstarred
let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(updatableReaderUnreadArticleIDs)
group.enter()
account.markAsUnstarred(deltaUnstarredArticleIDs) { _ in
group.leave()
}
try? await account.markAsUnstarred(deltaUnstarredArticleIDs)
completion()
group.notify(queue: DispatchQueue.main) {
completion()
}
} catch {
os_log(.error, log: self.log, "Sync Article Starred Status failed: %@.", error.localizedDescription)
}