diff --git a/Mac/Inspector/BuiltinSmartFeedInspectorViewController.swift b/Mac/Inspector/BuiltinSmartFeedInspectorViewController.swift index 3c6c7b908..72bca9099 100644 --- a/Mac/Inspector/BuiltinSmartFeedInspectorViewController.swift +++ b/Mac/Inspector/BuiltinSmartFeedInspectorViewController.swift @@ -8,7 +8,7 @@ import AppKit -final class BuiltinSmartFeedInspectorViewController: NSViewController, Inspector { +@MainActor final class BuiltinSmartFeedInspectorViewController: NSViewController, Inspector { @IBOutlet var nameTextField: NSTextField? @IBOutlet weak var smartFeedImageView: NSImageView! diff --git a/Mac/Inspector/FolderInspectorViewController.swift b/Mac/Inspector/FolderInspectorViewController.swift index 63f76b0d7..6e12f248d 100644 --- a/Mac/Inspector/FolderInspectorViewController.swift +++ b/Mac/Inspector/FolderInspectorViewController.swift @@ -10,7 +10,7 @@ import AppKit import Account import RSCore -final class FolderInspectorViewController: NSViewController, Inspector { +@MainActor final class FolderInspectorViewController: NSViewController, Inspector { @IBOutlet var nameTextField: NSTextField? @IBOutlet weak var folderImageView: NSImageView! diff --git a/Mac/Inspector/InspectorWindowController.swift b/Mac/Inspector/InspectorWindowController.swift index 2686f86c4..078de1439 100644 --- a/Mac/Inspector/InspectorWindowController.swift +++ b/Mac/Inspector/InspectorWindowController.swift @@ -8,7 +8,7 @@ import AppKit -protocol Inspector: AnyObject { +@MainActor protocol Inspector: AnyObject { var objects: [Any]? { get set } var isFallbackInspector: Bool { get } // Can handle nothing-to-inspect or unexpected type of objects. @@ -20,7 +20,7 @@ protocol Inspector: AnyObject { typealias InspectorViewController = Inspector & NSViewController -final class InspectorWindowController: NSWindowController { +@MainActor final class InspectorWindowController: NSWindowController { class var shouldOpenAtStartup: Bool { return UserDefaults.standard.bool(forKey: DefaultsKey.windowIsOpen) diff --git a/Mac/Inspector/NothingInspectorViewController.swift b/Mac/Inspector/NothingInspectorViewController.swift index 376e6a594..168513e5e 100644 --- a/Mac/Inspector/NothingInspectorViewController.swift +++ b/Mac/Inspector/NothingInspectorViewController.swift @@ -8,7 +8,7 @@ import AppKit -final class NothingInspectorViewController: NSViewController, Inspector { +@MainActor final class NothingInspectorViewController: NSViewController, Inspector { @IBOutlet var nothingTextField: NSTextField? @IBOutlet var multipleTextField: NSTextField? diff --git a/Mac/Inspector/WebFeedInspectorViewController.swift b/Mac/Inspector/WebFeedInspectorViewController.swift index 58c24b254..123961828 100644 --- a/Mac/Inspector/WebFeedInspectorViewController.swift +++ b/Mac/Inspector/WebFeedInspectorViewController.swift @@ -11,7 +11,7 @@ import Articles import Account import UserNotifications -final class WebFeedInspectorViewController: NSViewController, Inspector { +@MainActor final class WebFeedInspectorViewController: NSViewController, Inspector { @IBOutlet weak var iconView: IconView! @IBOutlet weak var nameTextField: NSTextField? diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index bf145a3b0..7592430f0 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -57,7 +57,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD var isSyncArticleStatusRunning = false var isWaitingForSyncTasks = false - override init() { + @MainActor override init() { super.init() appDelegate = self @@ -76,7 +76,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil) } - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + @MainActor func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { AppDefaults.registerDefaults() let isFirstRun = AppDefaults.shared.isFirstRun @@ -124,7 +124,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } - func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + @MainActor func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { DispatchQueue.main.async { self.resumeDatabaseProcessingIfNecessary() AccountManager.shared.receiveRemoteNotification(userInfo: userInfo) { @@ -134,17 +134,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } } - func applicationWillTerminate(_ application: UIApplication) { + @MainActor func applicationWillTerminate(_ application: UIApplication) { shuttingDown = true } - func applicationDidEnterBackground(_ application: UIApplication) { + @MainActor func applicationDidEnterBackground(_ application: UIApplication) { IconImageCache.shared.emptyCache() } // MARK: Notifications - @objc func unreadCountDidChange(_ note: Notification) { + @MainActor @objc func unreadCountDidChange(_ note: Notification) { if note.object is AccountManager { unreadCount = AccountManager.shared.unreadCount } @@ -152,21 +152,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // MARK: - API - func manualRefresh(errorHandler: @escaping (Error) -> ()) { + @MainActor func manualRefresh(errorHandler: @escaping (Error) -> ()) { UIApplication.shared.connectedScenes.compactMap( { $0.delegate as? SceneDelegate } ).forEach { $0.cleanUp(conditional: true) } AccountManager.shared.refreshAll(errorHandler: errorHandler) } - func resumeDatabaseProcessingIfNecessary() { + @MainActor func resumeDatabaseProcessingIfNecessary() { if AccountManager.shared.isSuspended { AccountManager.shared.resumeAll() logger.info("Application processing resumed.") } } - func prepareAccountsForBackground() { + @MainActor func prepareAccountsForBackground() { extensionFeedAddRequestFile.suspend() syncTimer?.invalidate() scheduleBackgroundFeedRefresh() @@ -175,7 +175,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD waitForSyncTasksToFinish() } - func prepareAccountsForForeground() { + @MainActor func prepareAccountsForForeground() { extensionFeedAddRequestFile.resume() syncTimer?.update() @@ -215,7 +215,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } - func presentThemeImportError(_ error: Error) { + @MainActor func presentThemeImportError(_ error: Error) { let windowScene = { let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene } return scenes.filter { $0.activationState == .foregroundActive }.first ?? scenes.first @@ -228,7 +228,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // MARK: App Initialization -private extension AppDelegate { +@MainActor private extension AppDelegate { private func initializeDownloaders() { let tempDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! diff --git a/iOS/KeyboardManager.swift b/iOS/KeyboardManager.swift index 981fa5eb4..0c3788be7 100644 --- a/iOS/KeyboardManager.swift +++ b/iOS/KeyboardManager.swift @@ -15,7 +15,7 @@ enum KeyboardType: String { case detail = "DetailKeyboardShortcuts" } -class KeyboardManager { +@MainActor final class KeyboardManager { private(set) var _keyCommands: [UIKeyCommand] var keyCommands: [UIKeyCommand] { diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift index c2e84c7c0..13eeac341 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift @@ -15,7 +15,7 @@ protocol MasterFeedTableViewCellDelegate: AnyObject { func masterFeedTableViewCellDisclosureDidToggle(_ sender: MasterFeedTableViewCell, expanding: Bool) } -class MasterFeedTableViewCell : VibrantTableViewCell { +@MainActor final class MasterFeedTableViewCell : VibrantTableViewCell { weak var delegate: MasterFeedTableViewCellDelegate? diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift index d4c7338bb..1f1e266fb 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCellLayout.swift @@ -9,7 +9,7 @@ import UIKit import RSCore -struct MasterFeedTableViewCellLayout { +@MainActor struct MasterFeedTableViewCellLayout { private static let indentWidth = CGFloat(integerLiteral: 15) private static let editingControlIndent = CGFloat(integerLiteral: 40) diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeaderLayout.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeaderLayout.swift index 73dce3f69..eeacff01f 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeaderLayout.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewSectionHeaderLayout.swift @@ -9,7 +9,7 @@ import UIKit import RSCore -struct MasterFeedTableViewSectionHeaderLayout { +@MainActor struct MasterFeedTableViewSectionHeaderLayout { private static let labelMarginRight = CGFloat(integerLiteral: 8) private static let disclosureButtonSize = CGSize(width: 44, height: 44) diff --git a/iOS/MasterFeed/Cell/MasterFeedUnreadCountView.swift b/iOS/MasterFeed/Cell/MasterFeedUnreadCountView.swift index 17db54492..ce2b7d912 100644 --- a/iOS/MasterFeed/Cell/MasterFeedUnreadCountView.swift +++ b/iOS/MasterFeed/Cell/MasterFeedUnreadCountView.swift @@ -8,7 +8,7 @@ import UIKit -class MasterFeedUnreadCountView : UIView { +@MainActor class MasterFeedUnreadCountView : UIView { var padding: UIEdgeInsets { return UIEdgeInsets(top: 1.0, left: 9.0, bottom: 1.0, right: 9.0) diff --git a/iOS/MasterFeed/RefreshProgressView.swift b/iOS/MasterFeed/RefreshProgressView.swift index 5ecd13e1e..0e72d7738 100644 --- a/iOS/MasterFeed/RefreshProgressView.swift +++ b/iOS/MasterFeed/RefreshProgressView.swift @@ -10,7 +10,7 @@ import SwiftUI import Account -struct RefreshProgressView: View { +@MainActor struct RefreshProgressView: View { static let width: CGFloat = 100 static let height: CGFloat = 5 diff --git a/iOS/MasterTimeline/MasterTimelineUnreadCountView.swift b/iOS/MasterTimeline/MasterTimelineUnreadCountView.swift index 66a7a4b26..a89b37fd7 100644 --- a/iOS/MasterTimeline/MasterTimelineUnreadCountView.swift +++ b/iOS/MasterTimeline/MasterTimelineUnreadCountView.swift @@ -8,7 +8,7 @@ import UIKit -class MasterTimelineUnreadCountView: MasterFeedUnreadCountView { +@MainActor final class MasterTimelineUnreadCountView: MasterFeedUnreadCountView { override var padding: UIEdgeInsets { return UIEdgeInsets(top: 2.0, left: 9.0, bottom: 2.0, right: 9.0) diff --git a/iOS/RootSplitViewController.swift b/iOS/RootSplitViewController.swift index 7b8c9d0c0..2c1075634 100644 --- a/iOS/RootSplitViewController.swift +++ b/iOS/RootSplitViewController.swift @@ -9,7 +9,7 @@ import UIKit import Account -class RootSplitViewController: UISplitViewController { +final class RootSplitViewController: UISplitViewController { var coordinator: SceneCoordinator! diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index 691d1ea24..fca5bc745 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -53,7 +53,7 @@ struct FeedNode: Hashable { } } -class SceneCoordinator: NSObject, UndoableCommandRunner, Logging { +final class SceneCoordinator: NSObject, UndoableCommandRunner, Logging { var undoableCommands = [UndoableCommand]() var undoManager: UndoManager? { diff --git a/iOS/SceneDelegate.swift b/iOS/SceneDelegate.swift index 85974c41a..1cd4c50bb 100644 --- a/iOS/SceneDelegate.swift +++ b/iOS/SceneDelegate.swift @@ -12,7 +12,7 @@ import Account import Zip import RSCore -class SceneDelegate: UIResponder, UIWindowSceneDelegate, Logging { +@MainActor final class SceneDelegate: UIResponder, UIWindowSceneDelegate, Logging { var window: UIWindow? var coordinator: SceneCoordinator! @@ -195,7 +195,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, Logging { do { try ArticleThemeDownloader.shared.handleFile(at: location) } catch { - self.presentError(error) + Task { @MainActor in + self.presentError(error) + } } } task.resume()