Remove most uses of AppDelegate.appDelegate on iOS.
This commit is contained in:
parent
2fed6d3cb3
commit
900cbabe71
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os
|
||||
import RSCore
|
||||
import RSWeb
|
||||
import Articles
|
||||
@ -90,6 +91,7 @@ public final class AccountManager: UnreadCountProvider {
|
||||
}
|
||||
|
||||
public let combinedRefreshProgress = CombinedRefreshProgress()
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "AccountManager")
|
||||
|
||||
public init(accountsFolder: String) {
|
||||
self.accountsFolder = accountsFolder
|
||||
@ -221,6 +223,13 @@ public final class AccountManager: UnreadCountProvider {
|
||||
}
|
||||
}
|
||||
|
||||
public func resumeAllIfSuspended() {
|
||||
if isSuspended {
|
||||
resumeAll()
|
||||
logger.info("Account processing resumed.")
|
||||
}
|
||||
}
|
||||
|
||||
public func resumeAll() {
|
||||
isSuspended = false
|
||||
for account in accounts {
|
||||
|
@ -15,7 +15,7 @@ struct UserInfoKey {
|
||||
static let articlePath = "articlePath"
|
||||
static let feedIdentifier = "feedIdentifier"
|
||||
static let draggedFeed = "draggedFeed" // DraggedFeed struct
|
||||
|
||||
static let errorHandler = "errorHandler" // ErrorHandlerBlock
|
||||
static let windowState = "windowState"
|
||||
static let windowFullScreenState = "windowFullScreenState"
|
||||
static let containerExpandedWindowState = "containerExpandedWindowState"
|
||||
|
@ -70,6 +70,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationC
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountRefreshDidFinish(_:)), name: .AccountRefreshDidFinish, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(userDidTriggerManualRefresh(_:)), name: .userDidTriggerManualRefresh, object: nil)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
@ -122,7 +123,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationC
|
||||
|
||||
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
||||
DispatchQueue.main.async {
|
||||
self.resumeDatabaseProcessingIfNecessary()
|
||||
AccountManager.shared.resumeAllIfSuspended()
|
||||
AccountManager.shared.receiveRemoteNotification(userInfo: userInfo) {
|
||||
self.suspendApplication()
|
||||
completionHandler(.newData)
|
||||
@ -150,22 +151,27 @@ final class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationC
|
||||
AppDefaults.lastRefresh = Date()
|
||||
}
|
||||
|
||||
@objc func userDidTriggerManualRefresh(_ note: Notification) {
|
||||
|
||||
guard let errorHandler = note.userInfo?[UserInfoKey.errorHandler] as? ErrorHandlerBlock else {
|
||||
assertionFailure("Expected errorHandler in .userDidTriggerManualRefresh userInfo")
|
||||
return
|
||||
}
|
||||
|
||||
manualRefresh(errorHandler: errorHandler)
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
|
||||
func manualRefresh(errorHandler: @escaping (Error) -> Void) {
|
||||
func manualRefresh(errorHandler: @escaping ErrorHandlerBlock) {
|
||||
|
||||
assert(Thread.isMainThread)
|
||||
UIApplication.shared.connectedScenes.compactMap( { $0.delegate as? SceneDelegate }).forEach {
|
||||
$0.cleanUp(conditional: true)
|
||||
}
|
||||
AccountManager.shared.refreshAll(errorHandler: errorHandler)
|
||||
}
|
||||
|
||||
func resumeDatabaseProcessingIfNecessary() {
|
||||
if AccountManager.shared.isSuspended {
|
||||
AccountManager.shared.resumeAll()
|
||||
logger.info("Application processing resumed.")
|
||||
}
|
||||
}
|
||||
|
||||
func prepareAccountsForBackground() {
|
||||
extensionFeedAddRequestFile.suspend()
|
||||
syncTimer?.invalidate()
|
||||
@ -408,7 +414,7 @@ private extension AppDelegate {
|
||||
let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String else {
|
||||
return
|
||||
}
|
||||
resumeDatabaseProcessingIfNecessary()
|
||||
AccountManager.shared.resumeAllIfSuspended()
|
||||
let account = AccountManager.shared.existingAccount(with: accountID)
|
||||
guard account != nil else {
|
||||
os_log(.debug, "No account found from notification.")
|
||||
@ -435,7 +441,7 @@ private extension AppDelegate {
|
||||
let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String else {
|
||||
return
|
||||
}
|
||||
resumeDatabaseProcessingIfNecessary()
|
||||
AccountManager.shared.resumeAllIfSuspended()
|
||||
let account = AccountManager.shared.existingAccount(with: accountID)
|
||||
guard account != nil else {
|
||||
os_log(.debug, "No account found from notification.")
|
||||
|
@ -10,11 +10,13 @@ import UIKit
|
||||
import RSCore
|
||||
import os.log
|
||||
|
||||
typealias ErrorHandlerBlock = (Error) -> Void
|
||||
|
||||
struct ErrorHandler {
|
||||
|
||||
private static var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Application")
|
||||
|
||||
public static func present(_ viewController: UIViewController) -> (Error) -> Void {
|
||||
public static func present(_ viewController: UIViewController) -> ErrorHandlerBlock {
|
||||
return { [weak viewController] error in
|
||||
if UIApplication.shared.applicationState == .active {
|
||||
viewController?.presentError(error)
|
||||
@ -27,5 +29,4 @@ struct ErrorHandler {
|
||||
public static func log(_ error: Error) {
|
||||
os_log(.error, log: self.log, "%@", error.localizedDescription)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -471,7 +471,7 @@ final class MainFeedViewController: UITableViewController, UndoableCommandRunner
|
||||
// This is a hack to make sure that an error dialog doesn't interfere with dismissing the refreshControl.
|
||||
// If the error dialog appears too closely to the call to endRefreshing, then the refreshControl never disappears.
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
appDelegate.manualRefresh(errorHandler: ErrorHandler.present(self))
|
||||
ManualRefreshNotification.post(errorHandler: ErrorHandler.present(self), sender: self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ final class TimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
// This is a hack to make sure that an error dialog doesn't interfere with dismissing the refreshControl.
|
||||
// If the error dialog appears too closely to the call to endRefreshing, then the refreshControl never disappears.
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
appDelegate.manualRefresh(errorHandler: ErrorHandler.present(self))
|
||||
ManualRefreshNotification.post(errorHandler: ErrorHandler.present(self), sender: self)
|
||||
}
|
||||
}
|
||||
|
||||
|
25
iOS/ManualRefreshNotification.swift
Normal file
25
iOS/ManualRefreshNotification.swift
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// ManualRefreshNotification.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Brent Simmons on 2/1/25.
|
||||
// Copyright © 2025 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Notification.Name {
|
||||
static let userDidTriggerManualRefresh = Notification.Name("userDidTriggerManualRefresh")
|
||||
}
|
||||
|
||||
// See UserInfoKey.errorHandler for the required ErrorHandler
|
||||
|
||||
struct ManualRefreshNotification {
|
||||
|
||||
static func post(errorHandler: @escaping ErrorHandlerBlock, sender: Any?) {
|
||||
Task { @MainActor in
|
||||
let userInfo = [UserInfoKey.errorHandler: errorHandler]
|
||||
NotificationCenter.default.post(name: .userDidTriggerManualRefresh, object: sender, userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
}
|
@ -116,7 +116,7 @@ final class RootSplitViewController: UISplitViewController {
|
||||
}
|
||||
|
||||
@objc func refresh(_ sender: Any?) {
|
||||
appDelegate.manualRefresh(errorHandler: ErrorHandler.present(self))
|
||||
ManualRefreshNotification.post(errorHandler: ErrorHandler.present(self), sender: self)
|
||||
}
|
||||
|
||||
@objc func goToToday(_ sender: Any?) {
|
||||
|
@ -267,7 +267,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
}
|
||||
|
||||
var isAnyUnreadAvailable: Bool {
|
||||
return appDelegate.unreadCount > 0
|
||||
return AccountManager.shared.unreadCount > 0
|
||||
}
|
||||
|
||||
var timelineUnreadCount: Int = 0
|
||||
@ -918,7 +918,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
|
||||
// This should never happen, but I don't want to risk throwing us
|
||||
// into an infinite loop searching for an unread that isn't there.
|
||||
if appDelegate.unreadCount < 1 {
|
||||
if AccountManager.shared.unreadCount < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
@ -939,7 +939,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
|
||||
// This should never happen, but I don't want to risk throwing us
|
||||
// into an infinite loop searching for an unread that isn't there.
|
||||
if appDelegate.unreadCount < 1 {
|
||||
if AccountManager.shared.unreadCount < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -65,13 +65,13 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
}
|
||||
|
||||
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||
AccountManager.shared.resumeAllIfSuspended()
|
||||
handleShortcutItem(shortcutItem)
|
||||
completionHandler(true)
|
||||
}
|
||||
|
||||
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
|
||||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||
AccountManager.shared.resumeAllIfSuspended()
|
||||
coordinator.handle(userActivity)
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene) {
|
||||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||
AccountManager.shared.resumeAllIfSuspended()
|
||||
appDelegate.prepareAccountsForForeground()
|
||||
coordinator.resetFocus()
|
||||
}
|
||||
@ -93,7 +93,7 @@ final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
// API
|
||||
|
||||
func handle(_ response: UNNotificationResponse) {
|
||||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||
AccountManager.shared.resumeAllIfSuspended()
|
||||
coordinator.handle(response)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user