Convert AccountDelegate.refreshAll to async/await.

This commit is contained in:
Brent Simmons 2024-03-26 17:31:46 -07:00
parent f6719d8b4f
commit 39f639244b
17 changed files with 153 additions and 128 deletions

View File

@ -421,22 +421,9 @@ public enum FetchType {
}
}
public func refreshAll(completion: @escaping (Result<Void, Error>) -> Void) {
delegate.refreshAll(for: self, completion: completion)
}
public func refreshAll() async throws {
try await withCheckedThrowingContinuation { continuation in
self.refreshAll { result in
switch result {
case .success:
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
try await delegate.refreshAll(for: self)
}
public func sendArticleStatus(completion: ((Result<Void, Error>) -> Void)? = nil) {
@ -481,7 +468,9 @@ public enum FetchType {
guard let self = self else { return }
// Reset the last fetch date to get the article history for the added feeds.
self.metadata.lastArticleFetchStartTime = nil
self.delegate.refreshAll(for: self, completion: completion)
Task { @MainActor in
try? await self.refreshAll()
}
case .failure(let error):
completion(.failure(error))
}

View File

@ -25,7 +25,7 @@ import Secrets
func receiveRemoteNotification(for account: Account, userInfo: [AnyHashable : Any], completion: @escaping () -> Void)
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void)
func refreshAll(for account: Account) async throws
func syncArticleStatus(for account: Account) async throws
func sendArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void))
func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void))

View File

@ -79,20 +79,28 @@ enum CloudKitAccountDelegateError: LocalizedError {
}
}
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
func refreshAll(for account: Account) async throws {
guard refreshProgress.isComplete else {
completion(.success(()))
return
}
let reachability = SCNetworkReachabilityCreateWithName(nil, "apple.com")
var flags = SCNetworkReachabilityFlags()
guard SCNetworkReachabilityGetFlags(reachability!, &flags), flags.contains(.reachable) else {
completion(.success(()))
return
}
standardRefreshAll(for: account, completion: completion)
try await withCheckedThrowingContinuation { continuation in
self.standardRefreshAll(for: account) { result in
switch result {
case .success:
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
func syncArticleStatus(for account: Account) async throws {
@ -582,15 +590,9 @@ private extension CloudKitAccountDelegate {
func combinedRefresh(_ account: Account, _ feeds: Set<Feed>, completion: @escaping (Result<Void, Error>) -> Void) {
let group = DispatchGroup()
group.enter()
refresher.refreshFeeds(feeds) {
group.leave()
}
group.notify(queue: DispatchQueue.main) {
completion(.success(()))
Task { @MainActor in
await self.refresher.refreshFeeds(feeds)
completion(.success(()))
}
}

View File

@ -79,37 +79,38 @@ final class FeedbinAccountDelegate: AccountDelegate {
completion()
}
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
func refreshAll(for account: Account) async throws {
refreshProgress.addToNumberOfTasksAndRemaining(5)
refreshAccount(account) { result in
switch result {
case .success():
self.refreshArticlesAndStatuses(account) { result in
switch result {
case .success():
completion(.success(()))
case .failure(let error):
DispatchQueue.main.async {
self.refreshProgress.clear()
let wrappedError = AccountError.wrappedError(error: error, account: account)
completion(.failure(wrappedError))
try await withCheckedThrowingContinuation { continuation in
refreshAccount(account) { result in
switch result {
case .success():
self.refreshArticlesAndStatuses(account) { result in
switch result {
case .success():
continuation.resume()
case .failure(let error):
DispatchQueue.main.async {
self.refreshProgress.clear()
let wrappedError = AccountError.wrappedError(error: error, account: account)
continuation.resume(throwing: wrappedError)
}
}
}
}
case .failure(let error):
DispatchQueue.main.async {
self.refreshProgress.clear()
let wrappedError = AccountError.wrappedError(error: error, account: account)
completion(.failure(wrappedError))
case .failure(let error):
DispatchQueue.main.async {
self.refreshProgress.clear()
let wrappedError = AccountError.wrappedError(error: error, account: account)
continuation.resume(throwing: wrappedError)
}
}
}
}
}
func syncArticleStatus(for account: Account) async throws {

View File

@ -108,7 +108,21 @@ final class FeedlyAccountDelegate: AccountDelegate {
completion()
}
@MainActor func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
func refreshAll(for account: Account) async throws {
try await withCheckedThrowingContinuation { continuation in
self.refreshAll(for: account) { result in
switch result {
case .success:
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
private func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
assert(Thread.isMainThread)
guard currentSyncAllOperation == nil else {

View File

@ -25,7 +25,7 @@ final class LocalAccountDelegate: AccountDelegate {
weak var account: Account?
private lazy var refresher: LocalAccountRefresher? = {
private lazy var refresher: LocalAccountRefresher = {
let refresher = LocalAccountRefresher()
refresher.delegate = self
return refresher
@ -44,28 +44,19 @@ final class LocalAccountDelegate: AccountDelegate {
completion()
}
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
func refreshAll(for account: Account) async throws {
guard refreshProgress.isComplete else {
completion(.success(()))
return
}
let feeds = account.flattenedFeeds()
refreshProgress.addToNumberOfTasksAndRemaining(feeds.count)
let group = DispatchGroup()
await refresher.refreshFeeds(feeds)
group.enter()
refresher?.refreshFeeds(feeds) {
group.leave()
}
group.notify(queue: DispatchQueue.main) {
self.refreshProgress.clear()
account.metadata.lastArticleFetchEndTime = Date()
completion(.success(()))
}
self.refreshProgress.clear()
account.metadata.lastArticleFetchEndTime = Date()
}
func syncArticleStatus(for account: Account) async throws {
@ -205,7 +196,7 @@ final class LocalAccountDelegate: AccountDelegate {
// MARK: Suspend and Resume (for iOS)
func suspendNetwork() {
refresher?.suspend()
refresher.suspend()
}
func suspendDatabase() {
@ -213,7 +204,7 @@ final class LocalAccountDelegate: AccountDelegate {
}
func resume() {
refresher?.resume()
refresher.resume()
}
}

View File

@ -28,7 +28,7 @@ final class LocalAccountRefresher {
return DownloadSession(delegate: self)
}()
public func refreshFeeds(_ feeds: Set<Feed>, completion: (() -> Void)? = nil) {
private func refreshFeeds(_ feeds: Set<Feed>, completion: (() -> Void)? = nil) {
guard !feeds.isEmpty else {
completion?()
return
@ -37,6 +37,15 @@ final class LocalAccountRefresher {
downloadSession.downloadObjects(feeds as NSSet)
}
public func refreshFeeds(_ feeds: Set<Feed>) async {
await withCheckedContinuation { continuation in
self.refreshFeeds(feeds) {
continuation.resume()
}
}
}
public func suspend() {
downloadSession.cancelAll()
isSuspended = true

View File

@ -61,7 +61,22 @@ final class NewsBlurAccountDelegate: AccountDelegate {
completion()
}
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> ()) {
func refreshAll(for account: Account) async throws {
try await withCheckedThrowingContinuation { continuation in
self.refreshAll(for: account) { result in
switch result {
case .success:
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
private func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> ()) {
self.refreshProgress.addToNumberOfTasksAndRemaining(4)
refreshFeeds(for: account) { result in

View File

@ -105,7 +105,21 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
completion()
}
func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
func refreshAll(for account: Account) async throws {
try await withCheckedThrowingContinuation { continuation in
self.refreshAll(for: account) { result in
switch result {
case .success:
continuation.resume()
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
private func refreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
refreshProgress.addToNumberOfTasksAndRemaining(6)
refreshAccount(account) { result in

View File

@ -102,16 +102,15 @@ class AccountsFeedbinWindowController: NSWindowController {
do {
try self.account?.removeCredentials(type: .basic)
try self.account?.storeCredentials(validatedCredentials)
self.account?.refreshAll() { result in
switch result {
case .success:
break
case .failure(let error):
Task { @MainActor in
do {
try await self.account?.refreshAll()
} catch {
NSApplication.shared.presentError(error)
}
}
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")

View File

@ -101,15 +101,14 @@ class AccountsNewsBlurWindowController: NSWindowController {
try self.account?.storeCredentials(credentials)
try self.account?.storeCredentials(validatedCredentials)
self.account?.refreshAll() { result in
switch result {
case .success:
break
case .failure(let error):
Task { @MainActor in
do {
try await self.account?.refreshAll()
} catch {
NSApplication.shared.presentError(error)
}
}
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")

View File

@ -271,11 +271,10 @@ extension AccountsPreferencesViewController: OAuthAccountAuthorizationOperationD
// because the user probably wants to see the result of authorizing NetNewsWire to act on their behalf.
NSApp.activate(ignoringOtherApps: true)
account.refreshAll { [weak self] result in
switch result {
case .success:
break
case .failure(let error):
Task { @MainActor [weak self] in
do {
try await account.refreshAll()
} catch {
self?.presentError(error)
}
}

View File

@ -157,16 +157,15 @@ class AccountsReaderAPIWindowController: NSWindowController {
try self.account?.removeCredentials(type: .readerAPIKey)
try self.account?.storeCredentials(credentials)
try self.account?.storeCredentials(validatedCredentials)
self.account?.refreshAll() { result in
switch result {
case .success:
break
case .failure(let error):
Task { @MainActor in
do {
try await self.account?.refreshAll()
} catch {
NSApplication.shared.presentError(error)
}
}
self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK)
} catch {
self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error")

View File

@ -139,16 +139,15 @@ class FeedbinAccountViewController: UITableViewController {
try self.account?.removeCredentials(type: .basic)
} catch {}
try self.account?.storeCredentials(credentials)
self.account?.refreshAll() { result in
switch result {
case .success:
break
case .failure(let error):
Task { @MainActor in
do {
try await self.account?.refreshAll()
} catch {
self.presentError(error)
}
}
self.dismiss(animated: true, completion: nil)
self.delegate?.dismiss()
} catch {

View File

@ -127,11 +127,10 @@ class NewsBlurAccountViewController: UITableViewController {
try self.account?.storeCredentials(basicCredentials)
try self.account?.storeCredentials(sessionCredentials)
self.account?.refreshAll() { result in
switch result {
case .success:
break
case .failure(let error):
Task { @MainActor in
do {
try await self.account?.refreshAll()
} catch {
self.presentError(error)
}
}

View File

@ -180,15 +180,14 @@ class ReaderAPIAccountViewController: UITableViewController {
self.dismiss(animated: true, completion: nil)
self.account?.refreshAll() { result in
switch result {
case .success:
break
case .failure(let error):
Task { @MainActor in
do {
try await self.account?.refreshAll()
} catch {
self.showError(NSLocalizedString(error.localizedDescription, comment: "Accoount Refresh Error"))
}
}
self.delegate?.dismiss()
} catch {
self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error"))

View File

@ -226,20 +226,17 @@ class AddAccountViewController: UITableViewController, AddAccountDismissDelegate
extension AddAccountViewController: OAuthAccountAuthorizationOperationDelegate {
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) {
let rootViewController = view.window?.rootViewController
account.refreshAll { result in
switch result {
case .success:
break
case .failure(let error):
guard let viewController = rootViewController else {
return
}
viewController.presentError(error)
Task { @MainActor in
do {
try await account.refreshAll()
} catch {
rootViewController?.presentError(error)
}
}
dismiss()
}