diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index ed6365030..aa2f57ac1 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -36,27 +36,6 @@ public enum AccountType: Int { // TODO: more } -public enum AccountError: LocalizedError { - - case createErrorNotFound - case createErrorAlreadySubscribed - case opmlImportInProgress - - public var errorDescription: String? { - switch self { - case .opmlImportInProgress: - return NSLocalizedString("An OPML import for this account is already running.", comment: "Import running") - default: - return NSLocalizedString("An unknown error occurred.", comment: "Unknown error") - } - } - - public var recoverySuggestion: String? { - return NSLocalizedString("Please try again later.", comment: "Try later") - } - -} - public final class Account: DisplayNameProvider, UnreadCountProvider, Container, Hashable { public struct UserInfoKey { @@ -309,7 +288,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, } } - public func refreshAll(completion: (() -> Void)? = nil) { + public func refreshAll(completion: @escaping (Result) -> Void) { self.delegate.refreshAll(for: self, completion: completion) } @@ -334,9 +313,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, guard let self = self else { return } // Reset the last fetch date to get the article history for the added feeds. self.metadata.lastArticleFetch = nil - self.delegate.refreshAll(for: self) { - completion(.success(())) - } + self.delegate.refreshAll(for: self, completion: completion) case .failure(let error): completion(.failure(error)) } @@ -457,8 +434,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, structureDidChange() DispatchQueue.main.async { - self.refreshAll() + self.refreshAll() { result in } } + } public func updateUnreadCounts(for feeds: Set) { diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index a86e081b8..4e8d20f58 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ 51D5875B227F630B00900287 /* tags_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58758227F630B00900287 /* tags_add.json */; }; 51D5875C227F630B00900287 /* tags_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58759227F630B00900287 /* tags_initial.json */; }; 51D5875E227F643C00900287 /* AccountFolderSyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D5875D227F643C00900287 /* AccountFolderSyncTest.swift */; }; + 51E3EB41229AF61B00645299 /* AccountError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB40229AF61B00645299 /* AccountError.swift */; }; 51E490362288C37100C791F0 /* FeedbinDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E490352288C37100C791F0 /* FeedbinDate.swift */; }; 51E59599228C77BC00FCC42B /* FeedbinUnreadEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E59598228C77BC00FCC42B /* FeedbinUnreadEntry.swift */; }; 51E5959B228C781500FCC42B /* FeedbinStarredEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E5959A228C781500FCC42B /* FeedbinStarredEntry.swift */; }; @@ -131,6 +132,7 @@ 51D58758227F630B00900287 /* tags_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_add.json; sourceTree = ""; }; 51D58759227F630B00900287 /* tags_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_initial.json; sourceTree = ""; }; 51D5875D227F643C00900287 /* AccountFolderSyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFolderSyncTest.swift; sourceTree = ""; }; + 51E3EB40229AF61B00645299 /* AccountError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountError.swift; sourceTree = ""; }; 51E490352288C37100C791F0 /* FeedbinDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinDate.swift; sourceTree = ""; }; 51E59598228C77BC00FCC42B /* FeedbinUnreadEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinUnreadEntry.swift; sourceTree = ""; }; 51E5959A228C781500FCC42B /* FeedbinStarredEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinStarredEntry.swift; sourceTree = ""; }; @@ -285,6 +287,7 @@ children = ( 848935101F62486800CEBD24 /* Account.swift */, 841974241F6DDCE4006346C4 /* AccountDelegate.swift */, + 51E3EB40229AF61B00645299 /* AccountError.swift */, 846E77531F6F00E300A165E2 /* AccountManager.swift */, 84AF4EA3222CFDD100F6A800 /* AccountMetadata.swift */, 84F73CF0202788D80000BCEF /* ArticleFetcher.swift */, @@ -530,6 +533,7 @@ 84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */, 5133231122810EB200C30F19 /* FeedbinIcon.swift in Sources */, 846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */, + 51E3EB41229AF61B00645299 /* AccountError.swift in Sources */, 51E59599228C77BC00FCC42B /* FeedbinUnreadEntry.swift in Sources */, 5165D72822835F7800D9D53D /* FeedFinder.swift in Sources */, 51D58755227F53BE00900287 /* FeedbinTag.swift in Sources */, diff --git a/Frameworks/Account/AccountDelegate.swift b/Frameworks/Account/AccountDelegate.swift index aaffefc16..314a944c5 100644 --- a/Frameworks/Account/AccountDelegate.swift +++ b/Frameworks/Account/AccountDelegate.swift @@ -22,7 +22,7 @@ protocol AccountDelegate { var refreshProgress: DownloadProgress { get } - func refreshAll(for account: Account, completion: (() -> Void)?) + func refreshAll(for account: Account, completion: @escaping (Result) -> Void) func sendArticleStatus(for account: Account, completion: @escaping (() -> Void)) func refreshArticleStatus(for account: Account, completion: @escaping (() -> Void)) diff --git a/Frameworks/Account/AccountError.swift b/Frameworks/Account/AccountError.swift new file mode 100644 index 000000000..d655a76f1 --- /dev/null +++ b/Frameworks/Account/AccountError.swift @@ -0,0 +1,63 @@ +// +// AccountError.swift +// Account +// +// Created by Maurice Parker on 5/26/19. +// Copyright © 2019 Ranchero Software, LLC. All rights reserved. +// + +import Foundation +import RSWeb + +public enum AccountError: LocalizedError { + + case createErrorNotFound + case createErrorAlreadySubscribed + case opmlImportInProgress + case wrappedError(error: Error, account: Account) + + public var errorDescription: String? { + switch self { + case .opmlImportInProgress: + return NSLocalizedString("An OPML import for this account is already running.", comment: "Import running") + case .wrappedError(let error, let account): + switch error { + case TransportError.httpError(let status): + if status == 401 { + let localizedText = NSLocalizedString("Your \"%@\" credentials are invalid or expired.", comment: "Invalid or expired") + return NSString.localizedStringWithFormat(localizedText as NSString, account.nameForDisplay) as String + } else { + return unknownError(error, account) + } + default: + return unknownError(error, account) + } + default: + return NSLocalizedString("An unknown error occurred.", comment: "Unknown error") + } + } + + public var recoverySuggestion: String? { + switch self { + case .wrappedError(let error, _): + switch error { + case TransportError.httpError(let status): + if status == 401 { + return NSLocalizedString("Please update your credentials for this account.", comment: "Try later") + } else { + return NSLocalizedString("Please try again later.", comment: "Try later") + } + default: + return NSLocalizedString("Please try again later.", comment: "Try later") + } + default: + return NSLocalizedString("Please try again later.", comment: "Try later") + } + } + + private func unknownError(_ error: Error, _ account: Account) -> String { + let localizedText = NSLocalizedString("An error occurred while processing the \"%@\" account: %@", comment: "Unknown error") + return NSString.localizedStringWithFormat(localizedText as NSString, account.nameForDisplay, error.localizedDescription) as String + } + +} diff --git a/Frameworks/Account/AccountManager.swift b/Frameworks/Account/AccountManager.swift index 48f7de3cb..7f72d5dd9 100644 --- a/Frameworks/Account/AccountManager.swift +++ b/Frameworks/Account/AccountManager.swift @@ -21,6 +21,7 @@ public final class AccountManager: UnreadCountProvider { public static let shared = AccountManager() public let defaultAccount: Account + private let accountsFolder = RSDataSubfolder(nil, "Accounts")! private var accountsDictionary = [String: Account]() @@ -145,9 +146,19 @@ public final class AccountManager: UnreadCountProvider { return accountsDictionary[accountID] } - public func refreshAll() { + public func refreshAll(errorHandler: @escaping (Error) -> Void) { - activeAccounts.forEach { $0.refreshAll() } + activeAccounts.forEach { account in + account.refreshAll() { result in + switch result { + case .success: + break + case .failure(let error): + errorHandler(error) + } + } + } + } public func syncArticleStatusAll(completion: (() -> Void)? = nil) { diff --git a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift index bf2ba57fd..1e172fde2 100644 --- a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift +++ b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift @@ -78,7 +78,7 @@ final class FeedbinAccountDelegate: AccountDelegate { var refreshProgress = DownloadProgress(numberOfTasks: 0) - func refreshAll(for account: Account, completion: (() -> Void)? = nil) { + func refreshAll(for account: Account, completion: @escaping (Result) -> Void) { refreshProgress.addToNumberOfTasksAndRemaining(6) @@ -91,7 +91,7 @@ final class FeedbinAccountDelegate: AccountDelegate { self.refreshMissingArticles(account) { self.refreshProgress.clear() DispatchQueue.main.async { - completion?() + completion(.success(())) } } } @@ -99,9 +99,9 @@ final class FeedbinAccountDelegate: AccountDelegate { case .failure(let error): DispatchQueue.main.async { - completion?() self.refreshProgress.clear() - self.handleError(error) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } @@ -222,7 +222,8 @@ final class FeedbinAccountDelegate: AccountDelegate { os_log(.debug, log: self.log, "Import OPML failed.") self.opmlImportInProgress = false DispatchQueue.main.async { - completion(.failure(error)) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } } @@ -240,7 +241,8 @@ final class FeedbinAccountDelegate: AccountDelegate { } case .failure(let error): DispatchQueue.main.async { - completion(.failure(error)) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } } @@ -274,7 +276,8 @@ final class FeedbinAccountDelegate: AccountDelegate { self.syncTaggings(account, taggings) case .failure(let error): DispatchQueue.main.async { - completion(.failure(error)) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } } @@ -302,7 +305,8 @@ final class FeedbinAccountDelegate: AccountDelegate { } case .failure(let error): DispatchQueue.main.async { - completion(.failure(error)) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } @@ -327,7 +331,8 @@ final class FeedbinAccountDelegate: AccountDelegate { } case .failure(let error): DispatchQueue.main.async { - completion(.failure(error)) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } } @@ -356,7 +361,8 @@ final class FeedbinAccountDelegate: AccountDelegate { } case .failure(let error): DispatchQueue.main.async { - completion(.failure(error)) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } } @@ -377,7 +383,8 @@ final class FeedbinAccountDelegate: AccountDelegate { } case .failure(let error): DispatchQueue.main.async { - completion(.failure(error)) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } } @@ -404,7 +411,8 @@ final class FeedbinAccountDelegate: AccountDelegate { } case .failure(let error): DispatchQueue.main.async { - completion(.failure(error)) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } } @@ -427,7 +435,8 @@ final class FeedbinAccountDelegate: AccountDelegate { self.processRestoredFeed(for: account, feed: feed, editedName: editedName, folder: folder, completion: completion) case .failure(let error): DispatchQueue.main.async { - completion(.failure(error)) + let wrappedError = AccountError.wrappedError(error: error, account: account) + completion(.failure(wrappedError)) } } } @@ -491,14 +500,6 @@ final class FeedbinAccountDelegate: AccountDelegate { private extension FeedbinAccountDelegate { - func handleError(_ error: Error) { - #if os(macOS) - NSApplication.shared.presentError(error) - #else - UIApplication.shared.presentError(error) - #endif - } - func refreshAccount(_ account: Account, completion: @escaping (Result) -> Void) { caller.retrieveTags { result in diff --git a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift index 3e9a97d81..1a35a1796 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift @@ -35,9 +35,9 @@ final class LocalAccountDelegate: AccountDelegate { } // LocalAccountDelegate doesn't wait for completion before calling the completion block - func refreshAll(for account: Account, completion: (() -> Void)? = nil) { + func refreshAll(for account: Account, completion: @escaping (Result) -> Void) { refresher.refreshFeeds(account.flattenedFeeds()) - completion?() + completion(.success(())) } func sendArticleStatus(for account: Account, completion: @escaping (() -> Void)) { diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index dc6a95206..269591996 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -334,7 +334,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, @IBAction func refreshAll(_ sender: Any?) { - AccountManager.shared.refreshAll() + AccountManager.shared.refreshAll(errorHandler: ErrorHandler.present) } @IBAction func showAddFeedWindow(_ sender: Any?) { diff --git a/Mac/ErrorHandler.swift b/Mac/ErrorHandler.swift new file mode 100644 index 000000000..f0258a7e2 --- /dev/null +++ b/Mac/ErrorHandler.swift @@ -0,0 +1,18 @@ +// +// ErrorHandler.swift +// NetNewsWire +// +// Created by Maurice Parker on 5/26/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import AppKit +import Account + +struct ErrorHandler { + + public static func present(_ error: Error) { + NSApplication.shared.presentError(error) + } + +} diff --git a/Mac/Preferences/Accounts/AccountsFeedbinWindowController.swift b/Mac/Preferences/Accounts/AccountsFeedbinWindowController.swift index 33b4b8375..11cdcc109 100644 --- a/Mac/Preferences/Accounts/AccountsFeedbinWindowController.swift +++ b/Mac/Preferences/Accounts/AccountsFeedbinWindowController.swift @@ -88,7 +88,14 @@ class AccountsFeedbinWindowController: NSWindowController { try self.account?.removeBasicCredentials() try self.account?.storeCredentials(credentials) if newAccount { - self.account?.refreshAll() + self.account?.refreshAll() { result in + switch result { + case .success: + break + case .failure(let error): + NSApplication.shared.presentError(error) + } + } } self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK) } catch { diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 6f8387122..bb3af139d 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -118,6 +118,8 @@ 51C452B42265141B00C03939 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51C452B32265141B00C03939 /* WebKit.framework */; }; 51C452B82265178500C03939 /* styleSheet.css in Resources */ = {isa = PBXBuildFile; fileRef = 51C452B72265178500C03939 /* styleSheet.css */; }; 51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; }; + 51E3EB33229AB02C00645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB32229AB02C00645299 /* ErrorHandler.swift */; }; + 51E3EB3D229AB08300645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB3C229AB08300645299 /* ErrorHandler.swift */; }; 51E595A5228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; }; 51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; }; 51E595AB228DF94C00FCC42B /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51E595AA228DF94C00FCC42B /* SettingsTableViewCell.xib */; }; @@ -711,6 +713,8 @@ 51C4528B2265095F00C03939 /* AddFolderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderViewController.swift; sourceTree = ""; }; 51C452B32265141B00C03939 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; 51C452B72265178500C03939 /* styleSheet.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = styleSheet.css; sourceTree = ""; }; + 51E3EB32229AB02C00645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = ""; }; + 51E3EB3C229AB08300645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = ""; }; 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleStatusSyncTimer.swift; sourceTree = ""; }; 51E595AA228DF94C00FCC42B /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = ""; }; 51E595AC228E1C2100FCC42B /* AddAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountViewController.swift; sourceTree = ""; }; @@ -1550,6 +1554,7 @@ 84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */, 849EE70E203919360082A1EA /* AppAssets.swift */, 842E45DC1ED8C54B000A8B52 /* Browser.swift */, + 51E3EB32229AB02C00645299 /* ErrorHandler.swift */, 842E45E11ED8C681000A8B52 /* MainWindow */, 84BBB12A20142A4700F054F5 /* Inspector */, 84C9FC6922629E1200D921D6 /* Preferences */, @@ -1665,6 +1670,7 @@ 840D617E2029031C009BC708 /* AppDelegate.swift */, 51C45254226507D200C03939 /* AppAssets.swift */, 51C45255226507D200C03939 /* AppDefaults.swift */, + 51E3EB3C229AB08300645299 /* ErrorHandler.swift */, 5126EE96226CB48A00C22AFC /* NavigationStateController.swift */, 51C4525D226508F600C03939 /* MasterFeed */, 51C4526D2265091600C03939 /* MasterTimeline */, @@ -2344,6 +2350,7 @@ 51C4529E22650A1900C03939 /* ImageDownloader.swift in Sources */, 51C45292226509C800C03939 /* TodayFeedDelegate.swift in Sources */, 51C452A222650A1900C03939 /* RSHTMLMetadata+Extension.swift in Sources */, + 51E3EB3D229AB08300645299 /* ErrorHandler.swift in Sources */, 5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */, 51EF0F7C2277919E0050506E /* TimelineNumberOfLinesViewController.swift in Sources */, 51C4529D22650A1000C03939 /* FaviconURLFinder.swift in Sources */, @@ -2458,6 +2465,7 @@ 849A97791ED9EC04007D329B /* TimelineStringFormatter.swift in Sources */, 84E185C3203BB12600F69BFA /* MultilineTextFieldSizer.swift in Sources */, 8477ACBE22238E9500DF7F37 /* SearchFeedDelegate.swift in Sources */, + 51E3EB33229AB02C00645299 /* ErrorHandler.swift in Sources */, 8472058120142E8900AD578B /* FeedInspectorViewController.swift in Sources */, 5144EA382279FC6200D19003 /* AccountsAddLocalWindowController.swift in Sources */, 84AD1EAA2031617300BC20B7 /* FolderPasteboardWriter.swift in Sources */, diff --git a/Shared/Timer/AccountRefreshTimer.swift b/Shared/Timer/AccountRefreshTimer.swift index 91c294f20..8a5af89ba 100644 --- a/Shared/Timer/AccountRefreshTimer.swift +++ b/Shared/Timer/AccountRefreshTimer.swift @@ -73,7 +73,7 @@ class AccountRefreshTimer { lastTimedRefresh = Date() update() - AccountManager.shared.refreshAll() + AccountManager.shared.refreshAll(errorHandler: ErrorHandler.present) } diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index a13689b43..0fa232d3f 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -175,10 +175,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele // If we haven't refreshed the database for 15 minutes, run a refresh automatically if let lastRefresh = AppDefaults.lastRefresh { if Date() > lastRefresh.addingTimeInterval(15 * 60) { - AccountManager.shared.refreshAll() + AccountManager.shared.refreshAll(errorHandler: ErrorHandler.present) } } else { - AccountManager.shared.refreshAll() + AccountManager.shared.refreshAll(errorHandler: ErrorHandler.present) } } @@ -222,7 +222,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele startingUnreadCount = self.unreadCount DispatchQueue.main.async { - AccountManager.shared.refreshAll() + AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log) } os_log("Accounts requested to begin refresh.", log: self.log, type: .debug) diff --git a/iOS/ErrorHandler.swift b/iOS/ErrorHandler.swift new file mode 100644 index 000000000..2bedf0a69 --- /dev/null +++ b/iOS/ErrorHandler.swift @@ -0,0 +1,25 @@ +// +// ErrorHandler.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 5/26/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import UIKit +import RSCore +import os.log + +struct ErrorHandler { + + private static var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Account") + + public static func present(_ error: Error) { + UIApplication.shared.presentError(error) + } + + public static func log(_ error: Error) { + os_log(.error, log: self.log, "%@", error.localizedDescription) + } + +} diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index cfb6fb1c2..73a654501 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -603,7 +603,7 @@ extension MasterFeedViewController: MasterFeedTableViewCellDelegate { private extension MasterFeedViewController { @objc private func refreshAccounts(_ sender: Any) { - AccountManager.shared.refreshAll() + AccountManager.shared.refreshAll(errorHandler: ErrorHandler.present) refreshControl?.endRefreshing() } diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index dc716ec7c..0ac2aa9cf 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -351,7 +351,7 @@ class MasterTimelineViewController: ProgressTableViewController, UndoableCommand private extension MasterTimelineViewController { @objc private func refreshAccounts(_ sender: Any) { - AccountManager.shared.refreshAll() + AccountManager.shared.refreshAll(errorHandler: ErrorHandler.present) refreshControl?.endRefreshing() } diff --git a/iOS/Settings/FeedbinAccountViewController.swift b/iOS/Settings/FeedbinAccountViewController.swift index 69c19c43e..d826c5086 100644 --- a/iOS/Settings/FeedbinAccountViewController.swift +++ b/iOS/Settings/FeedbinAccountViewController.swift @@ -72,10 +72,19 @@ class FeedbinAccountViewController: UIViewController { } do { + try self.account?.removeBasicCredentials() try self.account?.storeCredentials(credentials) + if newAccount { - self.account?.refreshAll() + self.account?.refreshAll() { result in + switch result { + case .success: + break + case .failure(let error): + UIApplication.shared.presentError(error) + } + } } self.delegate?.dismiss(self)