Convert update methods to async await.

This commit is contained in:
Brent Simmons 2024-04-04 21:15:13 -07:00
parent 3f2db0ef12
commit a88d57952e
7 changed files with 89 additions and 111 deletions

View File

@ -784,58 +784,36 @@ public enum FetchType {
}
@discardableResult
func update(_ feed: Feed, with parsedFeed: ParsedFeed) async throws -> ArticleChanges {
func update(feed: Feed, with parsedFeed: ParsedFeed) async throws -> ArticleChanges {
try await withCheckedThrowingContinuation { continuation in
self.update(feed, with: parsedFeed) { result in
switch result {
case .success(let articleChanges):
continuation.resume(returning: articleChanges)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
func update(_ feed: Feed, with parsedFeed: ParsedFeed, _ completion: @escaping UpdateArticlesCompletionBlock) {
// Used only by an On My Mac or iCloud account.
precondition(Thread.isMainThread)
precondition(type == .onMyMac || type == .cloudKit)
feed.takeSettings(from: parsedFeed)
let parsedItems = parsedFeed.items
guard !parsedItems.isEmpty else {
completion(.success(ArticleChanges()))
return
if parsedItems.isEmpty {
return ArticleChanges()
}
update(feed.feedID, with: parsedItems, completion: completion)
return try await update(feedID: feed.feedID, with: parsedItems)
}
func update(_ feedID: String, with parsedItems: Set<ParsedItem>, deleteOlder: Bool = true, completion: @escaping UpdateArticlesCompletionBlock) {
// Used only by an On My Mac or iCloud account.
func update(feedID: String, with parsedItems: Set<ParsedItem>, deleteOlder: Bool = true) async throws -> ArticleChanges {
precondition(Thread.isMainThread)
precondition(type == .onMyMac || type == .cloudKit)
database.update(with: parsedItems, feedID: feedID, deleteOlder: deleteOlder) { updateArticlesResult in
MainActor.assumeIsolated {
switch updateArticlesResult {
case .success(let articleChanges):
self.sendNotificationAbout(articleChanges)
completion(.success(articleChanges))
case .failure(let databaseError):
completion(.failure(databaseError))
}
}
}
let articleChanges = try await database.update(parsedItems: parsedItems, feedID: feedID, deleteOlder: deleteOlder)
self.sendNotificationAbout(articleChanges)
return articleChanges
}
func update(feedIDsAndItems: [String: Set<ParsedItem>], defaultRead: Bool, completion: @escaping DatabaseCompletionBlock) {
// Used only by syncing systems.
precondition(Thread.isMainThread)
precondition(type != .onMyMac && type != .cloudKit)
guard !feedIDsAndItems.isEmpty else {
completion(nil)
return

View File

@ -849,45 +849,43 @@ private extension CloudKitAccountDelegate {
InitialFeedDownloader.download(url) { parsedFeed in
self.refreshProgress.completeTask()
if let parsedFeed = parsedFeed {
account.update(feed, with: parsedFeed) { result in
MainActor.assumeIsolated {
switch result {
case .success:
self.accountZone.createFeed(url: bestFeedSpecifier.urlString,
name: parsedFeed.title,
editedName: editedName,
homePageURL: parsedFeed.homePageURL,
container: container) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let externalID):
feed.externalID = externalID
self.sendNewArticlesToTheCloud(account, feed)
completion(.success(feed))
case .failure(let error):
container.removeFeed(feed)
self.refreshProgress.completeTasks(2)
completion(.failure(error))
}
if let parsedFeed {
Task { @MainActor in
do {
try await account.update(feed: feed, with: parsedFeed)
self.accountZone.createFeed(url: bestFeedSpecifier.urlString,
name: parsedFeed.title,
editedName: editedName,
homePageURL: parsedFeed.homePageURL,
container: container) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let externalID):
feed.externalID = externalID
self.sendNewArticlesToTheCloud(account, feed)
completion(.success(feed))
case .failure(let error):
container.removeFeed(feed)
self.refreshProgress.completeTasks(2)
completion(.failure(error))
}
case .failure(let error):
container.removeFeed(feed)
self.refreshProgress.completeTasks(3)
completion(.failure(error))
}
} catch {
container.removeFeed(feed)
self.refreshProgress.completeTasks(3)
completion(.failure(error))
}
}
} else {
self.refreshProgress.completeTasks(3)
container.removeFeed(feed)
completion(.failure(AccountError.createErrorNotFound))
}
}
}
case .failure:

View File

@ -139,27 +139,27 @@ private extension CloudKitArticlesZoneDelegate {
let parsedItems = records.compactMap { self.makeParsedItem($0) }
let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
DispatchQueue.main.async {
Task { @MainActor in
for (feedID, parsedItems) in feedIDsAndItems {
group.enter()
self.account?.update(feedID, with: parsedItems, deleteOlder: false) { result in
Task { @MainActor in
switch result {
case .success(let articleChanges):
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()
case .failure(let databaseError):
errorOccurred = true
os_log(.error, log: self.log, "Error occurred while storing articles: %@", databaseError.localizedDescription)
group.leave()
}
do {
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()
}
}
}

View File

@ -227,7 +227,7 @@ private extension LocalAccountDelegate {
feed.editedName = editedName
container.addFeed(feed)
try await account.update(feed, with: parsedFeed)
try await account.update(feed: feed, with: parsedFeed)
return feed
}

View File

@ -109,24 +109,26 @@ extension LocalAccountRefresher: DownloadSessionDelegate {
return
}
account.update(feed, with: parsedFeed) { result in
MainActor.assumeIsolated {
if case .success(let articleChanges) = result {
if let httpResponse = response as? HTTPURLResponse {
feed.conditionalGetInfo = HTTPConditionalGetInfo(urlResponse: httpResponse)
}
feed.contentHash = dataHash
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
self.delegate?.localAccountRefresher(self, articleChanges: articleChanges) {
completion()
}
} else {
completion()
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
Task { @MainActor in
do {
let articleChanges = try await account.update(feed: feed, with: parsedFeed)
if let httpResponse = response as? HTTPURLResponse {
feed.conditionalGetInfo = HTTPConditionalGetInfo(urlResponse: httpResponse)
}
feed.contentHash = dataHash
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
self.delegate?.localAccountRefresher(self, articleChanges: articleChanges) {
completion()
}
} catch {
completion()
self.delegate?.localAccountRefresher(self, requestCompletedFor: feed)
}
}
}
}

View File

@ -14,7 +14,7 @@ import Parser
public typealias UnreadCountDictionary = [String: Int] // feedID: unreadCount
public struct ArticleChanges {
public struct ArticleChanges: Sendable {
public let newArticles: Set<Article>?
public let updatedArticles: Set<Article>?
public let deletedArticles: Set<Article>?

View File

@ -25,17 +25,17 @@ public extension ArticlesDatabase {
// MARK: - Saving, Updating, and Deleting Articles
/// Update articles and save new ones  for feed-based systems (local and iCloud).
nonisolated func update(with parsedItems: Set<ParsedItem>, feedID: String, deleteOlder: Bool, completion: @escaping UpdateArticlesCompletionBlock) {
Task {
do {
let articleChanges = try await update(parsedItems: parsedItems, feedID: feedID, deleteOlder: deleteOlder)
callUpdateArticlesCompletion(completion, .success(articleChanges))
} catch {
callUpdateArticlesCompletion(completion, .failure(.suspended))
}
}
}
// nonisolated func update(with parsedItems: Set<ParsedItem>, feedID: String, deleteOlder: Bool, completion: @escaping UpdateArticlesCompletionBlock) {
//
// Task {
// do {
// let articleChanges = try await update(parsedItems: parsedItems, feedID: feedID, deleteOlder: deleteOlder)
// callUpdateArticlesCompletion(completion, .success(articleChanges))
// } catch {
// callUpdateArticlesCompletion(completion, .failure(.suspended))
// }
// }
// }
/// Update articles and save new ones for sync systems (Feedbin, Feedly, etc.).
nonisolated func update(feedIDsAndItems: [String: Set<ParsedItem>], defaultRead: Bool, completion: @escaping UpdateArticlesCompletionBlock) {